claude-mem 13.9.0 → 13.9.2

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,"sources":["../src/sdk/index.ts","../src/storage/postgres/pool.ts","../src/storage/postgres/config.ts","../src/storage/postgres/schema.ts","../src/storage/postgres/utils.ts","../src/storage/postgres/agent-events.ts","../src/storage/postgres/auth.ts","../src/storage/postgres/generation-jobs.ts","../src/storage/postgres/observations.ts","../src/storage/postgres/projects.ts","../src/storage/postgres/server-sessions.ts","../src/storage/postgres/teams.ts","../src/storage/postgres/index.ts","../src/services/sync/ChromaMcpManager.ts","../src/utils/logger.ts","../src/shared/paths.ts","../src/shared/hook-io.ts","../src/shared/SettingsDefaultsManager.ts","../src/shared/hook-constants.ts","../src/supervisor/env-sanitizer.ts","../src/supervisor/index.ts","../src/supervisor/process-registry.ts","../src/shared/spawn.ts","../src/supervisor/shutdown.ts","../src/supervisor/health-checker.ts","../src/services/sync/ChromaSyncState.ts","../src/services/sync/ChromaSync.ts","../src/server/jobs/job-id.ts","../src/server/jobs/types.ts","../src/server/runtime/SessionGenerationPolicy.ts","../src/server/services/IngestEventsService.ts","../src/services/domain/ModeManager.ts","../src/server/generation/providers/shared/error-classification.ts","../src/utils/tag-stripping.ts","../src/server/generation/providers/shared/prompt-builder.ts","../src/server/generation/providers/ClaudeObservationProvider.ts","../src/server/generation/providers/GeminiObservationProvider.ts","../src/shared/openrouter-base-url.ts","../src/server/generation/providers/OpenRouterObservationProvider.ts","../src/sdk/parser.ts","../src/server/generation/processGeneratedResponse.ts"],"sourcesContent":["// cmem-sdk public API. Plan: plans/2026-05-25-cmem-sdk-and-server-rename.md §3-7.\n//\n// `createCmemClient(options)` builds the full in-process graph — pool →\n// schema bootstrap → repositories → tenancy resolution → Chroma (REQUIRED)\n// — and returns a CmemClient with every I/O method implemented: capture,\n// captureBatch, generate, captureAndGenerate, search, context,\n// startSession, endSession, and close.\n//\n// Existing internals in this directory (parser.ts, prompts.ts,\n// commit-verification.ts, hardened-options.ts, output-classifier.ts) are\n// reused internally (parser.ts + prompts.ts back generation). They are\n// intentionally NOT re-exported from the public surface here.\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\n\nimport {\n createPostgresPool,\n closePostgresPool,\n withPostgresTransaction,\n type PostgresPool,\n} from '../storage/postgres/pool.js';\nimport { parsePostgresConfig } from '../storage/postgres/config.js';\nimport { bootstrapServerPostgresSchema } from '../storage/postgres/schema.js';\nimport {\n createPostgresStorageRepositories,\n type PostgresStorageRepositories,\n} from '../storage/postgres/index.js';\nimport type { CreatePostgresAgentEventInput } from '../storage/postgres/agent-events.js';\nimport { ChromaSync, type ChromaDocument } from '../services/sync/ChromaSync.js';\nimport { ChromaMcpManager } from '../services/sync/ChromaMcpManager.js';\nimport { logger } from '../utils/logger.js';\nimport { IngestEventsService } from '../server/services/IngestEventsService.js';\nimport { ModeManager } from '../services/domain/ModeManager.js';\nimport {\n ClaudeObservationProvider,\n type ClaudeObservationProviderOptions,\n} from '../server/generation/providers/ClaudeObservationProvider.js';\nimport { GeminiObservationProvider } from '../server/generation/providers/GeminiObservationProvider.js';\nimport { OpenRouterObservationProvider } from '../server/generation/providers/OpenRouterObservationProvider.js';\nimport type {\n ServerGenerationProvider,\n ServerGenerationResult,\n} from '../server/generation/providers/shared/types.js';\nimport { processGeneratedResponse } from '../server/generation/processGeneratedResponse.js';\nimport type { PostgresObservation } from '../storage/postgres/observations.js';\n\n// ---------------------------------------------------------------------------\n// Public type re-exports. Plan: plans/2026-05-25-cmem-sdk-and-server-rename.md §7\n// line 263 — \"re-export `PostgresObservation`, the capture input type, search\n// result/context types, and the relevant `src/core/schemas` Zod types. Keep\n// the surface small and stable.\"\n//\n// Rules:\n// - Type-only (`export type`) for storage/core shapes — consumers should\n// read these as data, not instantiate Zod validators against them. This\n// also keeps the SDK bundle from gaining new runtime imports of\n// `src/core/schemas/*` (those modules pull `zod` at runtime).\n// - Only the entity types a Phase 8 SDK consumer would actually name\n// when they `import { ... } from 'claude-mem/sdk'` — implementation\n// helpers (`PostgresStorageRepositories` is already re-exported via the\n// `repos` field's structural type, internal `IngestEventsService`,\n// `*Row` interfaces, deterministic-key builders) stay private.\n// ---------------------------------------------------------------------------\n\n// Postgres storage entity types. `PostgresObservation` is the headline\n// re-export — every public result-bearing SDK method returns these. The\n// other three are the rest of the rows surfaced through `client.repos.*` so\n// consumers can name them without reaching into `claude-mem` internals.\nexport type {\n PostgresObservation,\n PostgresObservationSource,\n ObservationSourceType,\n} from '../storage/postgres/observations.js';\nexport type {\n PostgresAgentEvent,\n CreatePostgresAgentEventInput,\n} from '../storage/postgres/agent-events.js';\nexport type { PostgresServerSession } from '../storage/postgres/server-sessions.js';\nexport type { PostgresProject } from '../storage/postgres/projects.js';\nexport type { PostgresTeam, PostgresTeamMember, PostgresTeamRole } from '../storage/postgres/teams.js';\nexport type {\n PostgresObservationGenerationJob,\n PostgresObservationGenerationJobEvent,\n ObservationGenerationJobSourceType,\n ObservationGenerationJobStatus,\n ObservationGenerationJobEventType,\n} from '../storage/postgres/generation-jobs.js';\n// `PostgresStorageRepositories` is referenced on the `CmemClient.repos`\n// field, so consumers using e.g. `client.repos.agentEvents.getByIdForScope(...)`\n// can name the shape themselves. The implementation factory\n// (`createPostgresStorageRepositories`) stays private — consumers go through\n// `createCmemClient`, not through the repo factory directly.\nexport type { PostgresStorageRepositories } from '../storage/postgres/index.js';\n\n// Core schema types. These are the storage-agnostic shapes from\n// `src/core/schemas/`; they're a stable surface for downstream consumers\n// that want to model claude-mem data without importing the Postgres-specific\n// row types. Type-only — the matching `*Schema` Zod values stay internal\n// to keep the runtime surface frozen.\nexport type {\n MemoryItem,\n CreateMemoryItem,\n MemoryItemKind,\n MemorySource,\n CreateMemorySource,\n MemorySourceType,\n} from '../core/schemas/memory-item.js';\nexport type {\n AgentEvent,\n CreateAgentEvent,\n AgentEventSourceType,\n} from '../core/schemas/agent-event.js';\nexport type { ServerSession, CreateServerSession, ServerSessionStatus } from '../core/schemas/session.js';\nexport type { Project, CreateProject } from '../core/schemas/project.js';\nexport type { Team, CreateTeam, TeamMember, CreateTeamMember, TeamRole } from '../core/schemas/team.js';\n\n/**\n * Tuning options for the required Chroma semantic-search engine.\n *\n * Chroma is REQUIRED (see plan Executive Decision). There is no\n * `enabled: false` toggle — these options exist only to tune the\n * Chroma engine (collection naming, mcp subprocess path, etc.).\n */\nexport interface ChromaOptions {\n /**\n * Optional override for the Chroma collection-name prefix. Defaults to\n * the `cm__<projectId>` convention used by the existing ChromaSync.\n */\n collectionPrefix?: string;\n\n /**\n * Optional override for the chroma-mcp executable path. Defaults to\n * resolving `uvx chroma-mcp` on PATH.\n */\n mcpPath?: string;\n}\n\n/**\n * Pre-constructed generation provider accepted by\n * {@link CmemClientOptions.provider}. Identical shape to the server's\n * `ServerGenerationProvider`. Re-exported under a friendly name so\n * SDK consumers don't have to reach into `claude-mem/server`.\n */\nexport type CmemProvider = ServerGenerationProvider;\n\n/**\n * Configuration object accepted by {@link CmemClientOptions.provider}.\n * The SDK uses this to construct the matching concrete provider\n * ({@link ClaudeObservationProvider}, `GeminiObservationProvider`, or\n * `OpenRouterObservationProvider`). Defaults to `provider: 'claude'`.\n *\n * Mirrors the env-driven shape used by\n * `buildServerGenerationProviderFromEnv()` in\n * `src/server/runtime/create-server-service.ts:247`.\n */\nexport interface CmemProviderConfig {\n /** Provider API key. Required. */\n apiKey: string;\n /** Optional model id override (e.g. `claude-sonnet-4-6`). */\n model?: string;\n /** Provider kind. Defaults to `'claude'`. */\n provider?: 'claude' | 'gemini' | 'openrouter';\n /** OpenRouter-only: optional OpenAI-compatible base URL. */\n baseUrl?: string;\n}\n\n/**\n * Options accepted by {@link createCmemClient}.\n *\n * See plan §3 for the full graph this builds (pool → schema bootstrap →\n * repositories → ChromaSync). Provide either `databaseUrl` or `pool`;\n * provide explicit `teamId`+`projectId` in production (else the SDK\n * bootstraps + persists defaults to `$CLAUDE_MEM_DATA_DIR/sdk-tenant.json`).\n */\nexport interface CmemClientOptions {\n /**\n * Postgres connection URL. Falls back to `CLAUDE_MEM_SERVER_DATABASE_URL`\n * when neither this nor `pool` is supplied.\n */\n databaseUrl?: string;\n\n /**\n * Pre-built `pg.Pool` (or `PostgresQueryable`-compatible) instance.\n * When supplied, the SDK does NOT close it on `client.close()`.\n *\n * Typed as `unknown` here to keep the public type surface free of a\n * runtime dependency on `pg` types in consumer projects — Phase 3\n * narrows internally when consumed.\n */\n pool?: unknown;\n\n /**\n * Tenant team identifier (UUID). When omitted, the SDK calls\n * `ensureDefaults()` and persists the resolved ID.\n */\n teamId?: string;\n\n /**\n * Tenant project identifier (UUID). When omitted, the SDK calls\n * `ensureDefaults()` and persists the resolved ID.\n */\n projectId?: string;\n\n /**\n * Optional human-readable team name used only when the SDK has to\n * create a default team.\n */\n teamName?: string;\n\n /**\n * Optional human-readable project name used only when the SDK has to\n * create a default project (i.e. neither `projectId` nor a persisted\n * tenant file is present).\n */\n projectName?: string;\n\n /**\n * Generation provider. Three accepted shapes:\n * 1. A pre-constructed {@link CmemProvider} (anything with the right\n * `.generate(context, signal?)` shape — typically a server-internal\n * provider class like {@link ClaudeObservationProvider}).\n * 2. A {@link CmemProviderConfig} `{ apiKey, model?, provider? }` —\n * the SDK instantiates the matching concrete provider for you.\n * 3. `undefined` — the SDK falls back to the env-driven resolution\n * mirroring `buildServerGenerationProviderFromEnv()` in\n * `create-server-service.ts:247`. Reading `CLAUDE_MEM_SERVER_PROVIDER`\n * + `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` / `OPENROUTER_API_KEY`\n * and `CLAUDE_MEM_SERVER_MODEL`. Defaults to Claude when\n * `ANTHROPIC_API_KEY` is set.\n *\n * When `client.generate(...)` is called without any of these resolving\n * to a usable provider, the call rejects with a clear error.\n */\n provider?: CmemProvider | CmemProviderConfig;\n\n /**\n * Chroma tuning. Chroma is REQUIRED; this object does NOT include an\n * `enabled` flag. See {@link ChromaOptions}.\n */\n chroma?: ChromaOptions;\n}\n\n/**\n * Response envelope for {@link CmemClient.search}.\n *\n * `observations` are hydrated {@link PostgresObservation} rows — the same\n * shape callers see when reading directly from `repos.observations.*`.\n *\n * `chroma: true` means the result came from the Chroma semantic engine\n * (the default and intended path). `chroma: false` + `degraded: true`\n * means Chroma failed at request time and the SDK fell through to the\n * Postgres FTS safety net. This is a RUNTIME state, not a config toggle;\n * a `logger.error('CHROMA', …)` is emitted whenever it happens so an\n * operator can investigate the chroma-mcp subprocess.\n *\n * `error` is only present on the degraded branch. See plan §6.\n */\nexport interface CmemSearchResponse {\n observations: PostgresObservation[];\n chroma: boolean;\n degraded: boolean;\n error?: { message: string };\n}\n\n/**\n * Response envelope for {@link CmemClient.context}. Mirrors the\n * `ServerV1PostgresRoutes` `/context` shape — observations + a single\n * `\\n\\n`-joined string. `degraded` propagates from the underlying\n * {@link CmemSearchResponse}.\n */\nexport interface CmemContextResponse {\n observations: PostgresObservation[];\n context: string;\n degraded: boolean;\n}\n\n/**\n * The friendly capture-event shape accepted by\n * {@link CmemClient.capture} / {@link CmemClient.captureBatch}. Phase 4\n * maps this to `CreatePostgresAgentEventInput`. `projectId` + `teamId`\n * are added by the SDK from the resolved tenancy, not by the caller.\n */\nexport interface CmemCaptureEvent {\n /** Source system label (e.g. 'custom-cli', 'my-bot'). Required. */\n sourceAdapter: string;\n /** Event type tag (e.g. 'message', 'tool-use'). Required. */\n eventType: string;\n /** Free-form payload. Will be JSON-serialized. */\n payload: Record<string, unknown>;\n /** When this event happened. Defaults to `new Date()`. */\n occurredAt?: Date | string | number;\n /** Caller's idempotency key for dedup. Optional but recommended. */\n sourceEventId?: string;\n /** Optional server-session id this event belongs to. */\n serverSessionId?: string;\n /** Optional platform-source label (e.g. 'claude-code', 'opencode'). */\n platformSource?: string;\n /** Free-form metadata. Defaults to `{}`. */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of a {@link CmemClient.capture} / `captureBatch` call. Mirrors\n * the underlying `IngestEventsService.ingestOne` return shape, projected\n * down to the IDs consumers actually want.\n *\n * `generationJobId` is the queued `observation_generation_jobs` row that\n * Phase 5's `generate(jobId)` will consume. The SDK never enqueues to\n * Redis/BullMQ, so the job stays in status `queued` until you run it.\n */\nexport interface CmemCaptureResult {\n agentEventId: string;\n generationJobId: string;\n}\n\n/**\n * Optional input accepted by {@link CmemClient.startSession}. Maps to\n * `PostgresServerSessionsRepository.create`'s tenant-scoped input.\n */\nexport interface CmemStartSessionInput {\n /** Caller-supplied external session id (used for idempotent dedup). */\n externalSessionId?: string;\n /** Caller-supplied content session id (used for idempotent dedup). */\n contentSessionId?: string;\n /** Agent identifier (e.g. user or assistant id). */\n agentId?: string;\n /** Agent type label. */\n agentType?: string;\n /** Platform-source label (e.g. 'claude-code', 'opencode'). */\n platformSource?: string;\n /** Free-form metadata. */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Identifier returned by {@link CmemClient.startSession}.\n */\nexport interface CmemSessionInfo {\n serverSessionId: string;\n}\n\n/**\n * Result of a {@link CmemClient.generate} call. Mirrors\n * `processGeneratedResponse`'s `'completed'` outcome — the persisted\n * observations and the provider/model that produced them.\n *\n * `observations: []` is a normal-successful outcome when the provider\n * decided there was nothing worth recording (privacy-skipped batch,\n * `<skip_summary />` response, etc.). The job is marked completed\n * either way.\n */\nexport interface CmemGenerateResult {\n jobId: string;\n observations: PostgresObservation[];\n providerLabel: string;\n modelId?: string;\n /** `true` when the response was a privacy/skip signal with no observations. */\n privateContentDetected: boolean;\n}\n\n/**\n * Result of {@link CmemClient.captureAndGenerate}. The IDs from the\n * underlying {@link CmemCaptureResult} plus the persisted\n * {@link CmemGenerateResult}.\n */\nexport interface CmemCaptureAndGenerateResult {\n agentEventId: string;\n generationJobId: string;\n result: CmemGenerateResult;\n}\n\n/**\n * Public client returned by {@link createCmemClient}. All methods are\n * implemented: construction/tenancy/`close()` plus the capture, generate,\n * search, context, and session I/O paths.\n */\nexport interface CmemClient {\n /** Resolved tenant team ID (UUID). */\n readonly teamId: string;\n /** Resolved tenant project ID (UUID). */\n readonly projectId: string;\n /** Repository facade over the Postgres storage layer. */\n readonly repos: PostgresStorageRepositories;\n /** The underlying `pg.Pool` instance the SDK is using. */\n readonly pool: PostgresPool;\n /** The constructed `ChromaSync` for semantic search (Phase 6 consumes). */\n readonly chromaSync: ChromaSync;\n /** Persist a single agent event + outbox generation job. */\n capture(event: CmemCaptureEvent): Promise<CmemCaptureResult>;\n /** Persist many events in a single transactional batch. */\n captureBatch(events: CmemCaptureEvent[]): Promise<CmemCaptureResult[]>;\n /**\n * Run the in-process generation pipeline for a queued job. Transitions\n * the job `queued → processing`, calls the configured provider, then\n * delegates to `processGeneratedResponse` which writes the observations\n * and marks the job `completed` in a single Postgres transaction.\n */\n generate(jobId: string): Promise<CmemGenerateResult>;\n /** Sugar for capture-then-generate. */\n captureAndGenerate(event: CmemCaptureEvent): Promise<CmemCaptureAndGenerateResult>;\n /** Semantic search with FTS runtime safety net. See plan §6. */\n search(input: { query: string; limit?: number }): Promise<CmemSearchResponse>;\n /** Search + join contents into a single `\\n\\n`-delimited context blob. */\n context(input: { query: string; limit?: number }): Promise<CmemContextResponse>;\n /** Begin a server session for grouping subsequent captures. */\n startSession(input?: CmemStartSessionInput): Promise<CmemSessionInfo>;\n /** End the named server session. Idempotent. */\n endSession(serverSessionId: string): Promise<void>;\n /** Close Chroma + pool (if SDK-owned). Safe to call repeatedly. */\n close(): Promise<void>;\n}\n\n/**\n * Resolve the claude-mem data directory without pulling in\n * `src/shared/paths.ts` (which would transitively reference the\n * worker's settings/logger layer). Mirrors the priority that\n * `paths.ts::resolveDataDir` follows for the SDK's narrow needs:\n * 1. `CLAUDE_MEM_DATA_DIR` env var\n * 2. `$HOME/.claude-mem`\n *\n * Kept inline so the SDK bundle stays free of the worker's runtime\n * surface. See plan §3, anti-pattern guards.\n */\nfunction resolveSdkDataDir(): string {\n const fromEnv = process.env.CLAUDE_MEM_DATA_DIR;\n if (fromEnv && fromEnv.length > 0) {\n return fromEnv;\n }\n return path.join(os.homedir(), '.claude-mem');\n}\n\ninterface ResolvedTenancy {\n teamId: string;\n projectId: string;\n}\n\n/**\n * Type guard that recognizes an already-constructed\n * {@link ServerGenerationProvider}. We don't `instanceof`-check the concrete\n * provider classes because consumers may pass a custom implementation —\n * the structural check (`.generate` + `.providerLabel`) is sufficient.\n */\nfunction isCmemProvider(value: unknown): value is ServerGenerationProvider {\n if (value === null || typeof value !== 'object') return false;\n const candidate = value as Record<string, unknown>;\n return (\n typeof candidate.providerLabel === 'string'\n && typeof candidate.generate === 'function'\n );\n}\n\n/**\n * Instantiate the concrete {@link ServerGenerationProvider} for a resolved\n * `{ apiKey, model?, provider?, baseUrl? }`. Shared by the config-object and\n * env-driven resolution paths. Mirrors the dispatch in\n * `src/server/runtime/create-server-service.ts:261-279`.\n */\nfunction instantiateProvider(opts: {\n apiKey: string;\n model?: string;\n provider?: string;\n baseUrl?: string;\n}): ServerGenerationProvider {\n const kind = (opts.provider ?? 'claude').toLowerCase();\n if (kind === 'claude' || kind === 'anthropic') {\n const o: ClaudeObservationProviderOptions = { apiKey: opts.apiKey };\n if (opts.model) o.model = opts.model;\n return new ClaudeObservationProvider(o);\n }\n if (kind === 'gemini') {\n const o: { apiKey: string; model?: string } = { apiKey: opts.apiKey };\n if (opts.model) o.model = opts.model;\n return new GeminiObservationProvider(o);\n }\n if (kind === 'openrouter') {\n const o: { apiKey: string; model?: string; baseUrl?: string } = { apiKey: opts.apiKey };\n if (opts.model) o.model = opts.model;\n if (opts.baseUrl) o.baseUrl = opts.baseUrl;\n return new OpenRouterObservationProvider(o);\n }\n throw new Error(\n `cmem-sdk: unsupported provider kind \"${opts.provider ?? 'claude'}\". ` +\n `Expected one of: \"claude\", \"gemini\", \"openrouter\".`\n );\n}\n\n/**\n * Env-driven provider resolution. Mirrors\n * `buildServerGenerationProviderFromEnv()` in\n * `src/server/runtime/create-server-service.ts:261-279`. Returns `null`\n * when no provider can be resolved from the environment — callers surface\n * a clear error at `generate()` time instead of failing at construction.\n */\nfunction buildProviderFromEnv(): ServerGenerationProvider | null {\n const explicit = (process.env.CLAUDE_MEM_SERVER_PROVIDER ?? '').trim().toLowerCase();\n const model = process.env.CLAUDE_MEM_SERVER_MODEL;\n try {\n if (explicit === 'claude' || explicit === 'anthropic' || explicit === '') {\n const apiKey =\n process.env.ANTHROPIC_API_KEY ?? process.env.CLAUDE_MEM_ANTHROPIC_API_KEY ?? '';\n if (apiKey) return instantiateProvider({ apiKey, model, provider: 'claude' });\n // No claude key. If claude was explicitly demanded, give up; if the\n // provider was unset (''), fall through to the gemini/openrouter scan so\n // an installer that only set GEMINI_API_KEY still works.\n if (explicit !== '') return null;\n }\n if (explicit === 'gemini' || (explicit === '' && process.env.GEMINI_API_KEY)) {\n const apiKey = process.env.GEMINI_API_KEY ?? process.env.CLAUDE_MEM_GEMINI_API_KEY ?? '';\n if (!apiKey) return null;\n return instantiateProvider({ apiKey, model, provider: 'gemini' });\n }\n if (explicit === 'openrouter' || (explicit === '' && process.env.OPENROUTER_API_KEY)) {\n const apiKey =\n process.env.OPENROUTER_API_KEY ?? process.env.CLAUDE_MEM_OPENROUTER_API_KEY ?? '';\n if (!apiKey) return null;\n const baseUrl =\n process.env.CLAUDE_MEM_OPENROUTER_BASE_URL ?? process.env.OPENROUTER_BASE_URL;\n return instantiateProvider({ apiKey, model, provider: 'openrouter', baseUrl });\n }\n } catch {\n return null;\n }\n return null;\n}\n\n/**\n * Resolve a {@link ServerGenerationProvider} from {@link CmemClientOptions.provider}.\n * Three branches, in order:\n * 1. Already-constructed provider → return as-is.\n * 2. {@link CmemProviderConfig} → instantiate via {@link instantiateProvider}.\n * 3. `undefined` → fall back to {@link buildProviderFromEnv}.\n * Returns `null` only when env resolution found nothing — `generate()` then\n * surfaces a clear \"no provider configured\" error.\n */\nfunction resolveProvider(\n optionsProvider: CmemClientOptions['provider']\n): ServerGenerationProvider | null {\n if (optionsProvider === undefined) {\n return buildProviderFromEnv();\n }\n if (isCmemProvider(optionsProvider)) {\n return optionsProvider;\n }\n // Must be CmemProviderConfig.\n return instantiateProvider(optionsProvider);\n}\n\n/**\n * Eager mode initialization. `parseAgentXml` (`src/sdk/parser.ts:105`)\n * calls `ModeManager.getInstance().getActiveMode()` with no fallback —\n * if no mode is loaded, it throws. We mirror `loadServerMode()` in\n * `src/server/runtime/create-server-service.ts:167-177` and load `'code'`\n * once at SDK construction so the failure (a missing/broken\n * `plugin/modes/code.json`) surfaces during `createCmemClient(...)`\n * instead of at every `generate()` call.\n *\n * Throws a clear cmem-sdk-tagged error if the mode cannot be loaded.\n */\nfunction initializeMode(): void {\n try {\n const mgr = ModeManager.getInstance();\n mgr.loadMode('code');\n mgr.getActiveMode();\n } catch (err) {\n const underlying = err instanceof Error ? err.message : String(err);\n throw new Error(\n 'cmem-sdk: failed to load default observation mode \"code\". ' +\n 'Generation requires a mode (parser at src/sdk/parser.ts:105 has no fallback). ' +\n 'Verify the bundled plugin/modes/code.json is present. ' +\n 'Underlying: ' +\n underlying\n );\n }\n}\n\n/**\n * Read-or-create the default `{teamId, projectId}` pair. Per plan §3\n * line 191, this is the headless equivalent of the server runtime's\n * implicit tenancy — it persists IDs to a state file under\n * `$CLAUDE_MEM_DATA_DIR/sdk-tenant.json` so subsequent SDK runs reuse\n * them without re-creating rows.\n *\n * Production consumers are expected to pass explicit `teamId`+`projectId`\n * via {@link CmemClientOptions} and bypass this path entirely.\n */\nasync function resolveTenancy(\n options: CmemClientOptions,\n pool: PostgresPool\n): Promise<ResolvedTenancy> {\n if (options.teamId && options.projectId) {\n return { teamId: options.teamId, projectId: options.projectId };\n }\n\n const stateFile = path.join(resolveSdkDataDir(), 'sdk-tenant.json');\n\n // 1. Try to reuse a previously persisted pair.\n try {\n const raw = fs.readFileSync(stateFile, 'utf8');\n const parsed = JSON.parse(raw) as { teamId?: unknown; projectId?: unknown };\n if (typeof parsed.teamId === 'string' && typeof parsed.projectId === 'string') {\n return { teamId: parsed.teamId, projectId: parsed.projectId };\n }\n } catch {\n // Missing or corrupt — fall through to create.\n }\n\n // 2. Create default team + project in one transaction so a half-write\n // can't leave an orphan team.\n const created = await withPostgresTransaction(pool, async (tx) => {\n const txRepos = createPostgresStorageRepositories(tx);\n const team = await txRepos.teams.create({ name: options.teamName ?? 'default' });\n const project = await txRepos.projects.create({\n teamId: team.id,\n name: options.projectName ?? 'default',\n });\n return { teamId: team.id, projectId: project.id };\n });\n\n // 3. Best-effort persist; failure to write the state file is not fatal —\n // it just means the next run will re-create the pair. Most callers\n // will pass explicit IDs in production anyway.\n try {\n fs.mkdirSync(path.dirname(stateFile), { recursive: true });\n fs.writeFileSync(stateFile, JSON.stringify(created, null, 2));\n } catch {\n // ignore\n }\n\n return created;\n}\n\n/**\n * Copy each `keys` entry from `source` to `target` only when it is defined.\n * Keeps the optional-field plumbing on `mapCaptureEvent` / `startSession`\n * one line each under `exactOptionalPropertyTypes`.\n */\nfunction assignDefined(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n keys: readonly string[],\n): void {\n for (const key of keys) {\n if (source[key] !== undefined) target[key] = source[key];\n }\n}\n\n/**\n * Build {@link ChromaDocument}s for the freshly-persisted observations and\n * index them into the per-tenant Chroma collection via the now-public\n * `ChromaSync.addDocuments` (which batches + delete-then-add reconciles on\n * the `already exist` race). The document `id` is the Postgres observation\n * UUID — one Chroma doc per observation, NOT the SQLite-shaped\n * `obs_<sqlite_id>_<field>` split that `syncObservation` produces.\n *\n * Indexing failure is degraded, not catastrophic: the observations are\n * already in Postgres. The next `client.search()` either hits Chroma on\n * the retry-batched delete+add path, or falls through to FTS via the\n * search-side safety net. We log a warning and continue. See plan §6\n * line 244-247.\n */\nasync function indexObservationsToChroma(\n chromaSync: ChromaSync,\n observations: PostgresObservation[],\n scope: { projectId: string; teamId: string },\n): Promise<void> {\n if (observations.length === 0) return;\n const docs: ChromaDocument[] = observations.map(observation => {\n const metadata: Record<string, string | number> = {\n projectId: scope.projectId,\n teamId: scope.teamId,\n kind: observation.kind,\n observationId: observation.id,\n observationType: observation.kind,\n // ChromaSync's clean step filters out empty strings (ChromaSync.ts:291-295),\n // so passing '' for missing server_session_id collapses cleanly to\n // metadata-absent rather than indexing a meaningless empty value.\n serverSessionId: observation.serverSessionId ?? '',\n createdAt: new Date(observation.createdAtEpoch).toISOString(),\n };\n return {\n id: observation.id,\n document: observation.content,\n metadata,\n };\n });\n try {\n await chromaSync.addDocuments(docs);\n } catch (err) {\n // addDocuments swallows per-batch errors internally and returns a\n // count; an outer throw means something more fundamental (ensure\n // collection / connect) failed. Log + continue: the observations are\n // still persisted in Postgres, search will degrade to FTS until the\n // operator restarts chroma-mcp.\n logger.error(\n 'CHROMA',\n 'observation indexing failed after generate(); observations are persisted but unsearchable in Chroma until reindex',\n { projectId: scope.projectId, teamId: scope.teamId, observationCount: observations.length },\n err as Error,\n );\n }\n}\n\n/**\n * Construct a {@link CmemClient}.\n *\n * Phase 3 wires:\n * 1. Pool (either supplied or built from `databaseUrl` /\n * `CLAUDE_MEM_SERVER_DATABASE_URL`).\n * 2. Idempotent schema bootstrap.\n * 3. Repository facade.\n * 4. Tenancy resolution (`teamId`+`projectId`).\n * 5. Chroma (REQUIRED) — `ChromaSync(projectId).ensureCollectionExists()`.\n * If `uvx chroma-mcp` cannot start, `createCmemClient` REJECTS and\n * cleans up the SDK-owned pool before throwing.\n *\n * The returned client's I/O methods (capture, generate, search, context,\n * sessions) are all implemented below.\n */\nexport async function createCmemClient(options: CmemClientOptions): Promise<CmemClient> {\n // 0. Mode initialization. `parseAgentXml` requires an active mode\n // (src/sdk/parser.ts:105 has no fallback). Loading at construction\n // surfaces missing-mode-file errors here instead of at every\n // generate() call. Consistent with the Chroma-required philosophy:\n // fail fast at construction, not silently at runtime.\n initializeMode();\n\n // 1. Pool — either consumer-supplied or SDK-owned.\n let pool: PostgresPool;\n let ownsPool = false;\n if (options.pool) {\n pool = options.pool as PostgresPool;\n } else {\n // Allow `databaseUrl` to short-circuit env lookup by temporarily\n // overlaying it onto process.env for `parsePostgresConfig`.\n const envOverlay: NodeJS.ProcessEnv = options.databaseUrl\n ? { ...process.env, CLAUDE_MEM_SERVER_DATABASE_URL: options.databaseUrl }\n : process.env;\n const cfg = parsePostgresConfig({ env: envOverlay });\n if (!cfg) {\n throw new Error(\n 'cmem-sdk: CLAUDE_MEM_SERVER_DATABASE_URL or options.databaseUrl is required'\n );\n }\n pool = createPostgresPool(cfg);\n ownsPool = true;\n }\n\n // 2. Idempotent schema bootstrap.\n try {\n await bootstrapServerPostgresSchema(pool);\n } catch (err) {\n if (ownsPool) {\n await closePostgresPool(pool).catch(() => {});\n }\n throw err;\n }\n\n // 3. Repository facade.\n const repos = createPostgresStorageRepositories(pool);\n\n // 4. Tenancy resolution.\n let teamId: string;\n let projectId: string;\n try {\n const tenancy = await resolveTenancy(options, pool);\n teamId = tenancy.teamId;\n projectId = tenancy.projectId;\n } catch (err) {\n if (ownsPool) {\n await closePostgresPool(pool).catch(() => {});\n }\n throw err;\n }\n\n // 5. Chroma — REQUIRED. ensureCollectionExists boots the chroma-mcp\n // subprocess (via ChromaMcpManager) and creates the per-project\n // collection. It rethrows any error other than \"already exists\", so a\n // subprocess that can't start surfaces here and rejects construction.\n const chromaSync = new ChromaSync(projectId);\n try {\n await chromaSync.ensureCollectionExists();\n } catch (err) {\n // Clean up everything the SDK owns before rejecting.\n await chromaSync.close().catch(() => {});\n if (ownsPool) {\n await closePostgresPool(pool).catch(() => {});\n }\n const underlying = err instanceof Error ? err.message : String(err);\n throw new Error(\n 'cmem-sdk: Chroma is required but uvx chroma-mcp could not start. ' +\n 'Install uv (https://docs.astral.sh/uv/) and ensure chroma-mcp is available. ' +\n 'Underlying: ' +\n underlying\n );\n }\n\n // 6. Build the IngestEventsService once at construction. `resolveEventQueue`\n // MUST return `null` so the outbox row is written but no BullMQ enqueue\n // is attempted. Generation never runs inline here — that is Phase 5.\n // See IngestEventsService.ts:235-238 for the queued_only short-circuit.\n const ingest = new IngestEventsService({\n pool,\n resolveEventQueue: () => null,\n });\n\n // 7. Resolve the generation provider (Phase 5). Best-effort at construction\n // so the SDK can still serve capture/search even without a provider; a\n // `client.generate(...)` call without a resolved provider rejects with a\n // clear error. Provider exceptions during resolution (e.g. a\n // `ClaudeObservationProvider` constructor rejecting because apiKey is\n // empty) bubble up here and abort construction.\n const provider = resolveProvider(options.provider);\n\n // 8. Build and return the client. All I/O methods (capture, captureBatch,\n // generate, captureAndGenerate, search, context, startSession,\n // endSession, close) are implemented below.\n let closed = false;\n\n function mapCaptureEvent(event: CmemCaptureEvent): CreatePostgresAgentEventInput {\n const input: CreatePostgresAgentEventInput = {\n projectId,\n teamId,\n sourceAdapter: event.sourceAdapter,\n eventType: event.eventType,\n payload: event.payload as CreatePostgresAgentEventInput['payload'],\n occurredAt: event.occurredAt ?? new Date(),\n };\n assignDefined(input as unknown as Record<string, unknown>, event as unknown as Record<string, unknown>, [\n 'sourceEventId',\n 'serverSessionId',\n 'platformSource',\n 'metadata',\n ]);\n return input;\n }\n\n const client: CmemClient = {\n teamId,\n projectId,\n repos,\n pool,\n chromaSync,\n async capture(event: CmemCaptureEvent): Promise<CmemCaptureResult> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n const result = await ingest.ingestOne(mapCaptureEvent(event), {\n source: 'cmem-sdk.capture',\n sourceAdapter: event.sourceAdapter,\n });\n // resolveEventQueue: () => null guarantees `outbox` is non-null and\n // `enqueueState` is 'queued_only' whenever the default `generate: true`\n // path runs (IngestEventsService.ts:236-238). If a future caller\n // disables generation we surface a clear error rather than a TS-cast lie.\n if (!result.outbox) {\n throw new Error(\n 'cmem-sdk: capture expected a queued outbox row but received none. ' +\n 'This indicates IngestEventsService was invoked with generate: false; ' +\n 'capture() must produce a generation job for Phase 5 consumption.'\n );\n }\n return {\n agentEventId: result.event.id,\n generationJobId: result.outbox.id,\n };\n },\n async captureBatch(events: CmemCaptureEvent[]): Promise<CmemCaptureResult[]> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n if (events.length === 0) {\n return [];\n }\n // ingestBatch wraps all writes in a single Postgres transaction\n // (IngestEventsService.ts:170-220), which is materially better than\n // looping ingestOne per event. Same resolveEventQueue: () => null\n // semantics apply to every row.\n const results = await ingest.ingestBatch(\n events.map(mapCaptureEvent),\n { source: 'cmem-sdk.captureBatch' }\n );\n return results.map((r, i) => {\n if (!r.outbox) {\n const sourceEventId = events[i]?.sourceEventId ?? '<no source_event_id>';\n throw new Error(\n 'cmem-sdk: captureBatch expected a queued outbox row for event ' +\n `${sourceEventId} but received none.`\n );\n }\n return {\n agentEventId: r.event.id,\n generationJobId: r.outbox.id,\n };\n });\n },\n async generate(jobId: string): Promise<CmemGenerateResult> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n if (!provider) {\n throw new Error(\n 'cmem-sdk: no generation provider is configured. ' +\n 'Pass options.provider to createCmemClient (either a constructed ' +\n 'ServerGenerationProvider or { apiKey, model?, provider? }), or set ' +\n 'CLAUDE_MEM_SERVER_PROVIDER + the matching API key environment variable.'\n );\n }\n if (typeof jobId !== 'string' || jobId.trim().length === 0) {\n throw new Error('cmem-sdk: generate requires a non-empty jobId string');\n }\n\n // Step 1: lock the outbox row. queued → processing is the ONLY legal\n // first-step transition (transitionStatus enforces it at the SQL\n // level; generation-jobs.ts:191-194). queued → completed throws.\n // Pre-load via getByIdForScope to give a precise \"not claimable\"\n // error rather than the generic \"transition was not applied\".\n let lockedJob = await repos.observationGenerationJobs.getByIdForScope({\n id: jobId,\n projectId,\n teamId,\n });\n if (!lockedJob) {\n throw new Error(\n `cmem-sdk: generation job ${jobId} not found in scope (projectId=${projectId}, teamId=${teamId})`\n );\n }\n if (lockedJob.status !== 'queued') {\n throw new Error(\n `cmem-sdk: generation job ${jobId} is not claimable (status=\"${lockedJob.status}\"). ` +\n `generate() requires a job in status \"queued\"; the row may have been claimed ` +\n `by another worker, already processed, or terminally failed.`\n );\n }\n\n const transitioned = await repos.observationGenerationJobs.transitionStatus({\n id: jobId,\n projectId,\n teamId,\n status: 'processing',\n lockedBy: 'cmem-sdk',\n });\n if (!transitioned) {\n throw new Error(\n `cmem-sdk: failed to lock generation job ${jobId}; it may have been ` +\n `claimed concurrently by another worker.`\n );\n }\n lockedJob = transitioned;\n\n // Step 2: load events tied to the job. Mirrors\n // ProviderObservationGenerator.loadEvents (lines 483-532) for the\n // 'agent_event' source type — the SDK only ever produces\n // 'agent_event' jobs via capture(), but we still scope the load\n // by tenancy.\n const loadedEvents = [];\n if (lockedJob.agentEventId) {\n const ev = await repos.agentEvents.getByIdForScope({\n id: lockedJob.agentEventId,\n projectId,\n teamId,\n });\n if (ev) loadedEvents.push(ev);\n }\n\n // Step 3: load the project for the prompt's `projectName`.\n const project = await repos.projects.getByIdForTeam(projectId, teamId);\n\n // Steps 4–5: call the provider and persist. Both can throw while the\n // row is in 'processing', and generate()'s queued-only guard can\n // never reclaim a 'processing' row — so any failure here is marked\n // terminally 'failed' (a legal processing→failed transition) before\n // re-throwing. That leaves a diagnosable row with the error recorded\n // in last_error instead of one stuck in 'processing' forever.\n let providerResult: ServerGenerationResult;\n let outcome: Extract<\n Awaited<ReturnType<typeof processGeneratedResponse>>,\n { kind: 'completed' }\n >;\n try {\n // Step 4: call the provider. The lifted core mirrors\n // ProviderObservationGenerator.ts:200-209 — no BullMQ payload, no\n // AbortSignal (consumers control their own timeouts via the\n // provider's fetchImpl), no scope/revocation audit.\n providerResult = await provider.generate({\n job: lockedJob,\n events: loadedEvents,\n project: {\n projectId,\n teamId,\n serverSessionId: lockedJob.serverSessionId,\n projectName: project?.name ?? null,\n },\n });\n\n // Step 5: persist via processGeneratedResponse (the single Postgres\n // transaction that writes observations + observation_sources,\n // then transitions processing → completed). Mirrors\n // ProviderObservationGenerator.ts:211-227 minus the\n // session_summary branch (SDK currently captures only agent_event\n // jobs) and minus the BullMQ identity-context fields.\n const persistInput: Parameters<typeof processGeneratedResponse>[0] = {\n pool,\n job: lockedJob,\n rawText: providerResult.rawText,\n providerLabel: providerResult.providerLabel,\n sourceAdapter: 'sdk',\n };\n if (providerResult.modelId !== undefined) {\n persistInput.modelId = providerResult.modelId;\n }\n const persisted = await processGeneratedResponse(persistInput);\n\n if (persisted.kind === 'parse_error') {\n // The provider returned text we couldn't parse. Surface a clear\n // error; the catch below transitions the row to 'failed'.\n throw new Error(\n `cmem-sdk: generation parse error for job ${persisted.jobId}: ${persisted.reason}`\n );\n }\n outcome = persisted;\n } catch (err) {\n await repos.observationGenerationJobs\n .transitionStatus({\n id: jobId,\n projectId,\n teamId,\n status: 'failed',\n lastError: { message: err instanceof Error ? err.message : String(err) },\n })\n .catch(() => {\n // The row was already moved on (e.g. completed inside step 5's\n // own transaction, or claimed by another worker). Nothing to\n // recover; preserve the original error for the caller.\n });\n throw err;\n }\n\n // Step 6 (Phase 6): index the freshly-persisted observations into\n // Chroma so subsequent client.search() calls can find them. Postgres\n // is already canonical at this point — losing the Chroma index is\n // degraded (next search falls through to FTS), not catastrophic, so\n // indexing failures log + continue instead of throwing. The opposite\n // policy would re-emit an error and roll back the user's already-\n // completed generate(), which is worse than a silent reindex later.\n if (outcome.observations.length > 0) {\n await indexObservationsToChroma(\n chromaSync,\n outcome.observations,\n { projectId, teamId },\n );\n }\n\n const result: CmemGenerateResult = {\n jobId: outcome.jobId,\n observations: outcome.observations,\n providerLabel: providerResult.providerLabel,\n privateContentDetected: outcome.privateContentDetected,\n };\n if (providerResult.modelId !== undefined) {\n result.modelId = providerResult.modelId;\n }\n return result;\n },\n async captureAndGenerate(event: CmemCaptureEvent): Promise<CmemCaptureAndGenerateResult> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n const captured = await client.capture(event);\n const generated = await client.generate(captured.generationJobId);\n return {\n agentEventId: captured.agentEventId,\n generationJobId: captured.generationJobId,\n result: generated,\n };\n },\n async search(input: { query: string; limit?: number }): Promise<CmemSearchResponse> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n const limit = input.limit ?? 10;\n const query = typeof input.query === 'string' ? input.query : '';\n\n // Empty-query path — no semantic intent to express. Mirror the\n // SearchManager filter-only branch (SearchManager.ts:165-176) by\n // returning the most recent observations for this tenant. Chroma is\n // never consulted here, so `chroma: false` (it is NOT degraded — this\n // is the intended filter-only behavior, not a Chroma failure).\n if (query.trim().length === 0) {\n const observations = await repos.observations.listByProject({\n projectId,\n teamId,\n limit,\n });\n return { observations, chroma: false, degraded: false };\n }\n\n // Default path — Chroma semantic. Per plan §6 line 240:\n // queryChromaRaw → UUID doc ids → hydrate via getByIdForScope.\n // We call ChromaMcpManager.callTool directly (not ChromaSync.queryChroma)\n // because the latter routes through deduplicateQueryResults() which\n // is hard-coded to SQLite-shaped `obs_<digits>_<field>` ids and would\n // silently drop our UUID-shaped doc ids. ChromaSync still owns the\n // collection lifecycle (ensureCollectionExists / close); only the\n // query parse differs.\n try {\n await chromaSync.ensureCollectionExists();\n const mgr = ChromaMcpManager.getInstance();\n // Per-tenant `where` filter: doc metadata carries projectId + teamId\n // so a future shared collection can stay safely scoped, even though\n // today's collection name is already `cm__<projectId>`.\n const whereFilter = {\n $and: [\n { projectId },\n { teamId },\n ],\n };\n const raw = (await mgr.callTool('chroma_query_documents', {\n collection_name: chromaSync.getCollectionName(),\n query_texts: [query],\n n_results: limit,\n where: whereFilter,\n include: ['documents', 'metadatas', 'distances'],\n })) as {\n ids?: string[][];\n documents?: string[][];\n metadatas?: Array<Array<Record<string, unknown> | null>>;\n distances?: number[][];\n };\n\n const docIds = raw?.ids?.[0] ?? [];\n if (docIds.length === 0) {\n return { observations: [], chroma: true, degraded: false };\n }\n\n // Hydrate via getByIdForScope. We preserve Chroma's rank order\n // because semantic distance is the whole reason we called Chroma.\n // listByProject would reorder by created_at and undo that.\n const hydrated: PostgresObservation[] = [];\n for (const docId of docIds) {\n if (typeof docId !== 'string') continue;\n const obs = await repos.observations.getByIdForScope({\n id: docId,\n projectId,\n teamId,\n });\n if (obs) hydrated.push(obs);\n }\n return { observations: hydrated, chroma: true, degraded: false };\n } catch (err) {\n // Runtime safety net — Chroma transiently died (subprocess exit,\n // ECONNREFUSED, etc.). Mirrors SearchManager.ts:255's catch-and-\n // degrade-once pattern. This is NOT a feature toggle: a successful\n // run never enters this branch. We log loudly so an operator can\n // investigate the uvx chroma-mcp subprocess. See plan §6 line 242\n // and the 2026-05-29 correction log.\n logger.error(\n 'CHROMA',\n 'semantic search failed; returning degraded FTS results — investigate uvx chroma-mcp',\n { projectId, teamId, query },\n err as Error,\n );\n const observations = await repos.observations.search({\n projectId,\n teamId,\n query,\n limit,\n });\n return {\n observations,\n chroma: false,\n degraded: true,\n error: { message: 'chroma-mcp transient failure' },\n };\n }\n },\n async context(input: { query: string; limit?: number }): Promise<CmemContextResponse> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n // Reuse search so the Chroma / degraded-FTS branching lives in exactly\n // one place. The context-pack format below copies the\n // ServerV1PostgresRoutes /v1/context handler verbatim\n // (ServerV1PostgresRoutes.ts:892-895): map → filter non-empty → join.\n const result = await client.search(input);\n const context = result.observations\n .map(observation => observation.content)\n .filter(text => typeof text === 'string' && text.length > 0)\n .join('\\n\\n');\n return {\n observations: result.observations,\n context,\n degraded: result.degraded,\n };\n },\n async startSession(input: CmemStartSessionInput = {}): Promise<CmemSessionInfo> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n const createInput: Parameters<typeof repos.sessions.create>[0] = {\n projectId,\n teamId,\n };\n assignDefined(createInput as unknown as Record<string, unknown>, input as unknown as Record<string, unknown>, [\n 'externalSessionId',\n 'contentSessionId',\n 'agentId',\n 'agentType',\n 'platformSource',\n 'metadata',\n ]);\n const session = await repos.sessions.create(createInput);\n return { serverSessionId: session.id };\n },\n async endSession(serverSessionId: string): Promise<void> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n // endSession is idempotent (server-sessions.ts:145-162). It returns\n // null only if the (id, projectId, teamId) tuple does not exist;\n // surface that as a clear error so callers don't silently no-op.\n const updated = await repos.sessions.endSession({\n id: serverSessionId,\n projectId,\n teamId,\n });\n if (!updated) {\n throw new Error(\n `cmem-sdk: endSession could not find server_session ${serverSessionId} ` +\n `for tenant (projectId=${projectId}, teamId=${teamId})`\n );\n }\n },\n async close() {\n if (closed) {\n return;\n }\n closed = true;\n await chromaSync.close().catch(() => {});\n if (ownsPool) {\n await closePostgresPool(pool).catch(() => {});\n }\n },\n };\n\n return client;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport pg, { type Pool as PgPool, type PoolClient as PgPoolClient } from 'pg';\nimport { parsePostgresConfig, type PostgresConfig } from './config.js';\n\nconst { Pool } = pg;\n\nexport type PostgresPool = PgPool;\nexport type PostgresPoolClient = PgPoolClient;\n\nlet sharedPool: PostgresPool | null = null;\n\nexport function createPostgresPool(config: PostgresConfig): PostgresPool {\n return new Pool({\n connectionString: config.connectionString,\n max: config.max,\n idleTimeoutMillis: config.idleTimeoutMillis,\n connectionTimeoutMillis: config.connectionTimeoutMillis,\n statement_timeout: config.statementTimeoutMillis,\n ssl: config.ssl\n });\n}\n\nexport function getSharedPostgresPool(options: { requireDatabaseUrl?: boolean } = {}): PostgresPool {\n if (sharedPool) {\n return sharedPool;\n }\n const config = parsePostgresConfig({ requireDatabaseUrl: options.requireDatabaseUrl ?? true });\n if (!config) {\n throw new Error('Postgres requires CLAUDE_MEM_SERVER_DATABASE_URL');\n }\n sharedPool = createPostgresPool(config);\n return sharedPool;\n}\n\nexport async function checkPostgresHealth(pool: PostgresPool): Promise<boolean> {\n try {\n await pool.query('SELECT 1');\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function withPostgresTransaction<T>(\n pool: PostgresPool,\n fn: (client: PostgresPoolClient) => Promise<T>\n): Promise<T> {\n const client = await pool.connect();\n try {\n await client.query('BEGIN');\n const result = await fn(client);\n await client.query('COMMIT');\n return result;\n } catch (error) {\n await client.query('ROLLBACK');\n throw error;\n } finally {\n client.release();\n }\n}\n\nexport async function closePostgresPool(pool: PostgresPool): Promise<void> {\n if (pool === sharedPool) {\n sharedPool = null;\n }\n await pool.end();\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nexport interface PostgresConfig {\n connectionString: string;\n max: number;\n idleTimeoutMillis: number;\n connectionTimeoutMillis: number;\n statementTimeoutMillis: number;\n ssl: boolean | { rejectUnauthorized: boolean };\n}\n\nexport interface ParsePostgresConfigOptions {\n env?: NodeJS.ProcessEnv;\n requireDatabaseUrl?: boolean;\n}\n\nconst DEFAULT_POOL_MAX = 10;\nconst DEFAULT_IDLE_TIMEOUT_MS = 30_000;\nconst DEFAULT_CONNECTION_TIMEOUT_MS = 5_000;\nconst DEFAULT_STATEMENT_TIMEOUT_MS = 30_000;\n\nexport function getPostgresDatabaseUrl(env: NodeJS.ProcessEnv = process.env): string | null {\n return env.CLAUDE_MEM_SERVER_DATABASE_URL || null;\n}\n\nexport function parsePostgresConfig(options: ParsePostgresConfigOptions = {}): PostgresConfig | null {\n const env = options.env ?? process.env;\n const connectionString = getPostgresDatabaseUrl(env);\n if (!connectionString) {\n if (options.requireDatabaseUrl) {\n throw new Error('Postgres requires CLAUDE_MEM_SERVER_DATABASE_URL');\n }\n return null;\n }\n\n return {\n connectionString,\n max: parsePositiveInt(env.CLAUDE_MEM_POSTGRES_POOL_MAX, DEFAULT_POOL_MAX),\n idleTimeoutMillis: parsePositiveInt(env.CLAUDE_MEM_POSTGRES_IDLE_TIMEOUT_MS, DEFAULT_IDLE_TIMEOUT_MS),\n connectionTimeoutMillis: parsePositiveInt(env.CLAUDE_MEM_POSTGRES_CONNECTION_TIMEOUT_MS, DEFAULT_CONNECTION_TIMEOUT_MS),\n statementTimeoutMillis: parsePositiveInt(env.CLAUDE_MEM_POSTGRES_STATEMENT_TIMEOUT_MS, DEFAULT_STATEMENT_TIMEOUT_MS),\n ssl: parseSsl(connectionString, env)\n };\n}\n\nfunction parsePositiveInt(value: string | undefined, fallback: number): number {\n if (!value) {\n return fallback;\n }\n const parsed = Number.parseInt(value, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\nfunction parseSsl(connectionString: string, env: NodeJS.ProcessEnv): boolean | { rejectUnauthorized: boolean } {\n if (env.CLAUDE_MEM_POSTGRES_SSL === 'disable' || env.PGSSLMODE === 'disable') {\n return false;\n }\n if (env.CLAUDE_MEM_POSTGRES_SSL === 'require' || env.PGSSLMODE === 'require') {\n return { rejectUnauthorized: false };\n }\n\n try {\n const url = new URL(connectionString);\n if (url.searchParams.get('sslmode') === 'require') {\n return { rejectUnauthorized: false };\n }\n } catch {\n return false;\n }\n\n return false;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { PostgresQueryable } from './utils.js';\n\nexport const SERVER_POSTGRES_SCHEMA_VERSION = 1;\n\n// Phase 1b (cmem-sdk rename): the TS constant is renamed but the table-name\n// strings remain on `server_beta_*` since they are persisted DDL identifiers.\n// Plan §1d will migrate the table names in a coordinated DDL change.\nexport const SERVER_POSTGRES_TABLES = [\n 'server_beta_schema_migrations',\n 'teams',\n 'projects',\n 'team_members',\n 'api_keys',\n 'audit_log',\n 'server_sessions',\n 'agent_events',\n 'observation_generation_jobs',\n 'observations',\n 'observation_sources',\n 'observation_generation_job_events'\n] as const;\n\nexport async function bootstrapServerPostgresSchema(client: PostgresQueryable): Promise<void> {\n if (isPostgresPool(client)) {\n const poolClient = await client.connect();\n try {\n await bootstrapServerPostgresSchema(poolClient);\n } finally {\n poolClient.release();\n }\n return;\n }\n\n await client.query('BEGIN');\n try {\n await client.query(PHASE_1_SCHEMA_SQL);\n await client.query(\n `\n INSERT INTO server_beta_schema_migrations (version, description)\n VALUES ($1, $2)\n ON CONFLICT (version) DO NOTHING\n `,\n [SERVER_POSTGRES_SCHEMA_VERSION, 'phase 1 postgres observation storage foundation']\n );\n await client.query('COMMIT');\n } catch (error) {\n await client.query('ROLLBACK');\n throw error;\n }\n}\n\ninterface PostgresPoolLike extends PostgresQueryable {\n connect(): Promise<PostgresQueryable & { release(): void }>;\n}\n\nfunction isPostgresPool(client: PostgresQueryable): client is PostgresPoolLike {\n const candidate = client as {\n connect?: unknown;\n release?: unknown;\n totalCount?: unknown;\n idleCount?: unknown;\n waitingCount?: unknown;\n };\n return (\n typeof candidate.connect === 'function'\n && typeof candidate.release !== 'function'\n && typeof candidate.totalCount === 'number'\n && typeof candidate.idleCount === 'number'\n && typeof candidate.waitingCount === 'number'\n );\n}\n\nconst PHASE_1_SCHEMA_SQL = `\nCREATE TABLE IF NOT EXISTS server_beta_schema_migrations (\n version INTEGER PRIMARY KEY,\n description TEXT NOT NULL,\n applied_at TIMESTAMPTZ NOT NULL DEFAULT now()\n);\n\nCREATE TABLE IF NOT EXISTS teams (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now()\n);\n\nCREATE TABLE IF NOT EXISTS projects (\n id TEXT PRIMARY KEY,\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n UNIQUE (id, team_id)\n);\n\nCREATE TABLE IF NOT EXISTS team_members (\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n user_id TEXT NOT NULL,\n role TEXT NOT NULL,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n PRIMARY KEY (team_id, user_id)\n);\n\nCREATE TABLE IF NOT EXISTS api_keys (\n id TEXT PRIMARY KEY,\n key_hash TEXT NOT NULL UNIQUE,\n team_id TEXT REFERENCES teams(id) ON DELETE CASCADE,\n project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,\n actor_id TEXT NOT NULL,\n scopes JSONB NOT NULL DEFAULT '[]'::jsonb,\n revoked_at TIMESTAMPTZ,\n expires_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n CHECK (project_id IS NULL OR team_id IS NOT NULL),\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS audit_log (\n id TEXT PRIMARY KEY,\n team_id TEXT REFERENCES teams(id) ON DELETE SET NULL,\n project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,\n actor_id TEXT,\n api_key_id TEXT REFERENCES api_keys(id) ON DELETE SET NULL,\n action TEXT NOT NULL,\n resource_type TEXT NOT NULL,\n resource_id TEXT,\n details JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n CHECK (project_id IS NULL OR team_id IS NOT NULL),\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE SET NULL\n);\n\nCREATE TABLE IF NOT EXISTS server_sessions (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n external_session_id TEXT,\n idempotency_key TEXT,\n content_session_id TEXT,\n agent_id TEXT,\n agent_type TEXT,\n platform_source TEXT,\n generation_status TEXT NOT NULL DEFAULT 'idle',\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n started_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n ended_at TIMESTAMPTZ,\n last_generated_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n UNIQUE (project_id, external_session_id),\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS agent_events (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n server_session_id TEXT REFERENCES server_sessions(id) ON DELETE SET NULL,\n source_adapter TEXT NOT NULL,\n source_event_id TEXT,\n idempotency_key TEXT NOT NULL,\n event_type TEXT NOT NULL,\n payload JSONB NOT NULL,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n occurred_at TIMESTAMPTZ NOT NULL,\n received_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n UNIQUE (idempotency_key),\n UNIQUE (id, project_id, team_id),\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS observation_generation_jobs (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n agent_event_id TEXT REFERENCES agent_events(id) ON DELETE CASCADE,\n source_type TEXT NOT NULL CHECK (source_type IN ('agent_event', 'session_summary', 'observation_reindex')),\n source_id TEXT NOT NULL,\n server_session_id TEXT REFERENCES server_sessions(id) ON DELETE SET NULL,\n job_type TEXT NOT NULL,\n status TEXT NOT NULL CHECK (status IN ('queued', 'processing', 'completed', 'failed', 'cancelled')),\n idempotency_key TEXT NOT NULL UNIQUE,\n bullmq_job_id TEXT UNIQUE,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n next_attempt_at TIMESTAMPTZ,\n locked_at TIMESTAMPTZ,\n locked_by TEXT,\n completed_at TIMESTAMPTZ,\n failed_at TIMESTAMPTZ,\n cancelled_at TIMESTAMPTZ,\n last_error JSONB,\n payload JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n CHECK (\n (source_type = 'agent_event' AND agent_event_id IS NOT NULL AND source_id = agent_event_id)\n OR\n (source_type = 'session_summary' AND agent_event_id IS NULL AND server_session_id IS NOT NULL AND source_id = server_session_id)\n OR\n (source_type = 'observation_reindex' AND agent_event_id IS NULL)\n ),\n FOREIGN KEY (agent_event_id, project_id, team_id) REFERENCES agent_events(id, project_id, team_id) ON DELETE CASCADE,\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS observations (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n server_session_id TEXT REFERENCES server_sessions(id) ON DELETE SET NULL,\n kind TEXT NOT NULL DEFAULT 'observation',\n content TEXT NOT NULL,\n content_search TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', content)) STORED,\n generation_key TEXT,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n embedding JSONB,\n created_by_job_id TEXT REFERENCES observation_generation_jobs(id) ON DELETE SET NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS observation_sources (\n id TEXT PRIMARY KEY,\n observation_id TEXT NOT NULL REFERENCES observations(id) ON DELETE CASCADE,\n agent_event_id TEXT REFERENCES agent_events(id) ON DELETE CASCADE,\n generation_job_id TEXT REFERENCES observation_generation_jobs(id) ON DELETE SET NULL,\n source_type TEXT NOT NULL CHECK (source_type IN ('agent_event', 'session_summary', 'observation_reindex', 'manual')),\n source_id TEXT NOT NULL,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n UNIQUE (observation_id, source_type, source_id),\n UNIQUE (source_type, source_id, generation_job_id, observation_id),\n CHECK (\n (source_type = 'agent_event' AND agent_event_id IS NOT NULL AND source_id = agent_event_id)\n OR\n (source_type <> 'agent_event' AND agent_event_id IS NULL)\n )\n);\n\nCREATE TABLE IF NOT EXISTS observation_generation_job_events (\n id TEXT PRIMARY KEY,\n generation_job_id TEXT NOT NULL REFERENCES observation_generation_jobs(id) ON DELETE CASCADE,\n event_type TEXT NOT NULL CHECK (event_type IN ('queued', 'enqueued', 'processing', 'retry_scheduled', 'completed', 'failed', 'cancelled')),\n status_after TEXT NOT NULL CHECK (status_after IN ('queued', 'processing', 'completed', 'failed', 'cancelled')),\n attempt INTEGER NOT NULL DEFAULT 0,\n details JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now()\n);\n\nCREATE INDEX IF NOT EXISTS idx_agent_events_project_session ON agent_events(project_id, server_session_id, occurred_at);\nALTER TABLE server_sessions ADD COLUMN IF NOT EXISTS idempotency_key TEXT;\n-- #2560 — platform_source on agent_events (consistent with server_sessions and\n-- the plan-09 scoping): which platform produced the event (claude-code,\n-- opencode, cursor, ...). Idempotent so an existing DB upgrades in place.\nALTER TABLE agent_events ADD COLUMN IF NOT EXISTS platform_source TEXT;\nCREATE INDEX IF NOT EXISTS idx_agent_events_platform_source\n ON agent_events(team_id, project_id, platform_source, occurred_at)\n WHERE platform_source IS NOT NULL;\nCREATE INDEX IF NOT EXISTS idx_server_sessions_platform_source\n ON server_sessions(team_id, project_id, platform_source, started_at)\n WHERE platform_source IS NOT NULL;\nALTER TABLE observations ADD COLUMN IF NOT EXISTS content_search TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', content)) STORED;\nALTER TABLE observations DROP CONSTRAINT IF EXISTS observations_generation_key_key;\nALTER TABLE observation_generation_jobs DROP CONSTRAINT IF EXISTS observation_generation_jobs_source_type_source_id_job_type_key;\nCREATE UNIQUE INDEX IF NOT EXISTS idx_server_sessions_project_idempotency\n ON server_sessions(project_id, idempotency_key)\n WHERE idempotency_key IS NOT NULL;\n-- Supports the session linkage lookup on the /v1/events ingest path\n-- (WHERE content_session_id = $1 AND project_id = $2 ...).\nCREATE INDEX IF NOT EXISTS idx_server_sessions_content_session\n ON server_sessions(project_id, content_session_id)\n WHERE content_session_id IS NOT NULL;\nCREATE UNIQUE INDEX IF NOT EXISTS idx_observations_generation_key_scope\n ON observations(team_id, project_id, generation_key)\n WHERE generation_key IS NOT NULL;\nCREATE UNIQUE INDEX IF NOT EXISTS idx_observation_jobs_source_scope\n ON observation_generation_jobs(team_id, project_id, source_type, source_id, job_type);\nCREATE INDEX IF NOT EXISTS idx_projects_team ON projects(team_id, id);\nCREATE INDEX IF NOT EXISTS idx_agent_events_team_project ON agent_events(team_id, project_id, occurred_at);\nCREATE INDEX IF NOT EXISTS idx_observations_project_session ON observations(project_id, server_session_id, created_at);\nCREATE INDEX IF NOT EXISTS idx_observations_team_project ON observations(team_id, project_id, created_at);\nCREATE INDEX IF NOT EXISTS idx_observations_content_search ON observations USING GIN (content_search);\nCREATE INDEX IF NOT EXISTS idx_observation_sources_event ON observation_sources(agent_event_id);\nCREATE INDEX IF NOT EXISTS idx_observation_sources_source ON observation_sources(source_type, source_id);\nCREATE INDEX IF NOT EXISTS idx_observation_jobs_status_next_attempt ON observation_generation_jobs(status, next_attempt_at, created_at);\nCREATE INDEX IF NOT EXISTS idx_observation_jobs_team_project ON observation_generation_jobs(team_id, project_id, status, created_at);\nCREATE INDEX IF NOT EXISTS idx_observation_jobs_event ON observation_generation_jobs(agent_event_id);\nCREATE INDEX IF NOT EXISTS idx_observation_jobs_source ON observation_generation_jobs(source_type, source_id);\nCREATE INDEX IF NOT EXISTS idx_observation_job_events_job_created ON observation_generation_job_events(generation_job_id, created_at);\nCREATE INDEX IF NOT EXISTS idx_audit_log_scope_created ON audit_log(project_id, team_id, created_at);\n`;\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { createHash, randomUUID } from 'crypto';\nimport type { QueryResult, QueryResultRow } from 'pg';\n\nexport type JsonObject = Record<string, unknown>;\nexport type JsonValue = unknown;\n\nexport interface PostgresQueryable {\n query<T extends QueryResultRow = QueryResultRow>(text: string, values?: unknown[]): Promise<QueryResult<T>>;\n}\n\nexport function newId(): string {\n return randomUUID();\n}\n\nexport function toJsonObject(value: unknown): JsonObject {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return value as JsonObject;\n }\n return {};\n}\n\nexport function toJsonArray(value: unknown): unknown[] {\n return Array.isArray(value) ? value : [];\n}\n\nexport function toEpoch(value: Date | string | number): number {\n if (typeof value === 'number') {\n return value;\n }\n return new Date(value).getTime();\n}\n\nexport function toDate(value: Date | string | number | null | undefined): Date | null {\n if (value == null) {\n return null;\n }\n return value instanceof Date ? value : new Date(value);\n}\n\nexport async function queryOne<T extends QueryResultRow>(\n client: PostgresQueryable,\n text: string,\n values: unknown[] = []\n): Promise<T | null> {\n const result = await client.query<T>(text, values);\n return result.rows[0] ?? null;\n}\n\nexport async function assertProjectOwnership(\n client: PostgresQueryable,\n projectId: string,\n teamId: string\n): Promise<void> {\n const row = await queryOne<{ id: string }>(\n client,\n 'SELECT id FROM projects WHERE id = $1 AND team_id = $2',\n [projectId, teamId]\n );\n if (!row) {\n throw new Error('project_id must belong to team_id');\n }\n}\n\nexport async function assertSessionOwnership(\n client: PostgresQueryable,\n serverSessionId: string,\n projectId: string,\n teamId: string\n): Promise<void> {\n const row = await queryOne<{ id: string }>(\n client,\n 'SELECT id FROM server_sessions WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [serverSessionId, projectId, teamId]\n );\n if (!row) {\n throw new Error('server_session_id must belong to project_id and team_id');\n }\n}\n\nexport function canonicalJson(value: unknown): string {\n return JSON.stringify(sortJson(value));\n}\n\nexport function deterministicKey(parts: readonly unknown[]): string {\n const fingerprint = createHash('sha256')\n .update(canonicalJson(parts))\n .digest('hex');\n return fingerprint;\n}\n\nfunction sortJson(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortJson);\n }\n if (value && typeof value === 'object') {\n const record = value as Record<string, unknown>;\n return Object.keys(record)\n .sort()\n .reduce<Record<string, unknown>>((acc, key) => {\n acc[key] = sortJson(record[key]);\n return acc;\n }, {});\n }\n return value;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, JsonValue, PostgresQueryable } from './utils.js';\nimport {\n assertProjectOwnership,\n assertSessionOwnership,\n canonicalJson,\n deterministicKey,\n newId,\n queryOne,\n toEpoch,\n toJsonObject\n} from './utils.js';\n\nexport interface PostgresAgentEvent {\n id: string;\n projectId: string;\n teamId: string;\n serverSessionId: string | null;\n sourceAdapter: string;\n sourceEventId: string | null;\n idempotencyKey: string;\n eventType: string;\n platformSource: string | null;\n payload: JsonValue;\n metadata: JsonObject;\n occurredAtEpoch: number;\n receivedAtEpoch: number;\n createdAtEpoch: number;\n}\n\nexport interface CreatePostgresAgentEventInput {\n id?: string;\n projectId: string;\n teamId: string;\n serverSessionId?: string | null;\n contentSessionId?: string | null;\n sourceAdapter: string;\n sourceEventId?: string | null;\n eventType: string;\n // #2560 — which platform produced the event (claude-code, opencode, ...).\n // Persisted on agent_events for plan-09 scoping. Optional; null when unknown.\n platformSource?: string | null;\n payload?: JsonValue;\n metadata?: JsonObject;\n occurredAt: Date | string | number;\n}\n\ninterface AgentEventRow {\n id: string;\n project_id: string;\n team_id: string;\n server_session_id: string | null;\n source_adapter: string;\n source_event_id: string | null;\n idempotency_key: string;\n event_type: string;\n platform_source: string | null;\n payload: unknown;\n metadata: unknown;\n occurred_at: Date;\n received_at: Date;\n created_at: Date;\n}\n\nexport class PostgresAgentEventsRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: CreatePostgresAgentEventInput): Promise<PostgresAgentEvent> {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n if (input.serverSessionId) {\n await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);\n }\n const idempotencyKey = buildAgentEventIdempotencyKey(input);\n const row = await queryOne<AgentEventRow>(\n this.client,\n `\n INSERT INTO agent_events (\n id, project_id, team_id, server_session_id, source_adapter,\n source_event_id, idempotency_key, event_type, platform_source, payload, metadata, occurred_at\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb, $11::jsonb, $12)\n ON CONFLICT (idempotency_key) DO UPDATE SET\n metadata = agent_events.metadata || excluded.metadata,\n platform_source = COALESCE(excluded.platform_source, agent_events.platform_source)\n RETURNING *\n `,\n [\n input.id ?? newId(),\n input.projectId,\n input.teamId,\n input.serverSessionId ?? null,\n input.sourceAdapter,\n input.sourceEventId ?? null,\n idempotencyKey,\n input.eventType,\n input.platformSource ?? null,\n JSON.stringify(input.payload ?? {}),\n JSON.stringify(input.metadata ?? {}),\n new Date(input.occurredAt)\n ]\n );\n return mapAgentEventRow(row!);\n }\n\n async getByIdForScope(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresAgentEvent | null> {\n const row = await queryOne<AgentEventRow>(\n this.client,\n 'SELECT * FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.id, input.projectId, input.teamId]\n );\n return row ? mapAgentEventRow(row) : null;\n }\n\n async listByProject(input: {\n projectId: string;\n teamId: string;\n serverSessionId?: string | null;\n limit?: number;\n }): Promise<PostgresAgentEvent[]> {\n const result = await this.client.query<AgentEventRow>(\n `\n SELECT * FROM agent_events\n WHERE project_id = $1\n AND team_id = $2\n AND ($3::text IS NULL OR server_session_id = $3)\n ORDER BY occurred_at DESC\n LIMIT $4\n `,\n [input.projectId, input.teamId, input.serverSessionId ?? null, input.limit ?? 100]\n );\n return result.rows.map(mapAgentEventRow);\n }\n}\n\nexport function buildAgentEventIdempotencyKey(input: {\n teamId: string;\n projectId: string;\n sourceAdapter: string;\n sourceEventId?: string | null;\n serverSessionId?: string | null;\n contentSessionId?: string | null;\n eventType: string;\n occurredAt: Date | string | number;\n payload?: JsonValue;\n}): string {\n if (input.sourceEventId) {\n return `agent_event:v1:${deterministicKey([\n input.teamId,\n input.projectId,\n input.sourceAdapter,\n input.sourceEventId\n ])}`;\n }\n\n // Use contentSessionId (stable, client-provided) over serverSessionId, which is\n // resolved lazily at ingest and can be NULL on a first delivery but non-NULL on a\n // retry — that would drift the key and create duplicate events. Falls back to\n // serverSessionId for events that don't carry a contentSessionId.\n return `agent_event:v1:${deterministicKey([\n input.teamId,\n input.projectId,\n input.sourceAdapter,\n input.contentSessionId ?? input.serverSessionId ?? null,\n input.eventType,\n new Date(input.occurredAt).toISOString(),\n canonicalJson(input.payload ?? {})\n ])}`;\n}\n\nfunction mapAgentEventRow(row: AgentEventRow): PostgresAgentEvent {\n return {\n id: row.id,\n projectId: row.project_id,\n teamId: row.team_id,\n serverSessionId: row.server_session_id,\n sourceAdapter: row.source_adapter,\n sourceEventId: row.source_event_id,\n idempotencyKey: row.idempotency_key,\n eventType: row.event_type,\n platformSource: row.platform_source,\n payload: row.payload,\n metadata: toJsonObject(row.metadata),\n occurredAtEpoch: toEpoch(row.occurred_at),\n receivedAtEpoch: toEpoch(row.received_at),\n createdAtEpoch: toEpoch(row.created_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, PostgresQueryable } from './utils.js';\nimport { assertProjectOwnership, newId, queryOne, toDate, toEpoch, toJsonArray, toJsonObject } from './utils.js';\n\nexport interface PostgresApiKey {\n id: string;\n keyHash: string;\n teamId: string | null;\n projectId: string | null;\n actorId: string;\n scopes: unknown[];\n revokedAtEpoch: number | null;\n expiresAtEpoch: number | null;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\nexport interface PostgresAuditLog {\n id: string;\n teamId: string | null;\n projectId: string | null;\n actorId: string | null;\n apiKeyId: string | null;\n action: string;\n resourceType: string;\n resourceId: string | null;\n details: JsonObject;\n createdAtEpoch: number;\n}\n\ninterface ApiKeyRow {\n id: string;\n key_hash: string;\n team_id: string | null;\n project_id: string | null;\n actor_id: string;\n scopes: unknown;\n revoked_at: Date | null;\n expires_at: Date | null;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface AuditLogRow {\n id: string;\n team_id: string | null;\n project_id: string | null;\n actor_id: string | null;\n api_key_id: string | null;\n action: string;\n resource_type: string;\n resource_id: string | null;\n details: unknown;\n created_at: Date;\n}\n\nexport class PostgresAuthRepository {\n constructor(private client: PostgresQueryable) {}\n\n async createApiKey(input: {\n id?: string;\n keyHash: string;\n teamId?: string | null;\n projectId?: string | null;\n actorId: string;\n scopes?: unknown[];\n expiresAt?: Date | null;\n }): Promise<PostgresApiKey> {\n if (input.projectId && input.teamId) {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n }\n const id = input.id ?? newId();\n const row = await queryOne<ApiKeyRow>(\n this.client,\n `\n INSERT INTO api_keys (id, key_hash, team_id, project_id, actor_id, scopes, expires_at)\n VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7)\n RETURNING *\n `,\n [\n id,\n input.keyHash,\n input.teamId ?? null,\n input.projectId ?? null,\n input.actorId,\n JSON.stringify(input.scopes ?? []),\n input.expiresAt ?? null\n ]\n );\n return mapApiKeyRow(row!);\n }\n\n async createAuditLog(input: {\n id?: string;\n teamId?: string | null;\n projectId?: string | null;\n actorId?: string | null;\n apiKeyId?: string | null;\n action: string;\n resourceType: string;\n resourceId?: string | null;\n details?: JsonObject;\n }): Promise<PostgresAuditLog> {\n if (input.projectId && input.teamId) {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n }\n const id = input.id ?? newId();\n const row = await queryOne<AuditLogRow>(\n this.client,\n `\n INSERT INTO audit_log (\n id, team_id, project_id, actor_id, api_key_id, action,\n resource_type, resource_id, details\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb)\n RETURNING *\n `,\n [\n id,\n input.teamId ?? null,\n input.projectId ?? null,\n input.actorId ?? null,\n input.apiKeyId ?? null,\n input.action,\n input.resourceType,\n input.resourceId ?? null,\n JSON.stringify(input.details ?? {})\n ]\n );\n return mapAuditLogRow(row!);\n }\n\n async getApiKeyByHash(keyHash: string): Promise<PostgresApiKey | null> {\n const row = await queryOne<ApiKeyRow>(this.client, 'SELECT * FROM api_keys WHERE key_hash = $1', [keyHash]);\n return row ? mapApiKeyRow(row) : null;\n }\n}\n\nfunction mapApiKeyRow(row: ApiKeyRow): PostgresApiKey {\n return {\n id: row.id,\n keyHash: row.key_hash,\n teamId: row.team_id,\n projectId: row.project_id,\n actorId: row.actor_id,\n scopes: toJsonArray(row.scopes),\n revokedAtEpoch: toDate(row.revoked_at)?.getTime() ?? null,\n expiresAtEpoch: toDate(row.expires_at)?.getTime() ?? null,\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n\nfunction mapAuditLogRow(row: AuditLogRow): PostgresAuditLog {\n return {\n id: row.id,\n teamId: row.team_id,\n projectId: row.project_id,\n actorId: row.actor_id,\n apiKeyId: row.api_key_id,\n action: row.action,\n resourceType: row.resource_type,\n resourceId: row.resource_id,\n details: toJsonObject(row.details),\n createdAtEpoch: toEpoch(row.created_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, PostgresQueryable } from './utils.js';\nimport {\n assertProjectOwnership,\n assertSessionOwnership,\n deterministicKey,\n newId,\n queryOne,\n toDate,\n toEpoch,\n toJsonObject\n} from './utils.js';\n\nexport type ObservationGenerationJobSourceType = 'agent_event' | 'session_summary' | 'observation_reindex';\nexport type ObservationGenerationJobStatus = 'queued' | 'processing' | 'completed' | 'failed' | 'cancelled';\nexport type ObservationGenerationJobEventType =\n | 'queued'\n | 'enqueued'\n | 'processing'\n | 'retry_scheduled'\n | 'completed'\n | 'failed'\n | 'cancelled';\n\nexport interface PostgresObservationGenerationJob {\n id: string;\n projectId: string;\n teamId: string;\n agentEventId: string | null;\n sourceType: ObservationGenerationJobSourceType;\n sourceId: string;\n serverSessionId: string | null;\n jobType: string;\n status: ObservationGenerationJobStatus;\n idempotencyKey: string;\n bullmqJobId: string | null;\n attempts: number;\n maxAttempts: number;\n nextAttemptAtEpoch: number | null;\n lockedAtEpoch: number | null;\n lockedBy: string | null;\n completedAtEpoch: number | null;\n failedAtEpoch: number | null;\n cancelledAtEpoch: number | null;\n lastError: JsonObject | null;\n payload: JsonObject;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\nexport interface PostgresObservationGenerationJobEvent {\n id: string;\n generationJobId: string;\n eventType: ObservationGenerationJobEventType;\n statusAfter: ObservationGenerationJobStatus;\n attempt: number;\n details: JsonObject;\n createdAtEpoch: number;\n}\n\ninterface JobRow {\n id: string;\n project_id: string;\n team_id: string;\n agent_event_id: string | null;\n source_type: ObservationGenerationJobSourceType;\n source_id: string;\n server_session_id: string | null;\n job_type: string;\n status: ObservationGenerationJobStatus;\n idempotency_key: string;\n bullmq_job_id: string | null;\n attempts: number;\n max_attempts: number;\n next_attempt_at: Date | null;\n locked_at: Date | null;\n locked_by: string | null;\n completed_at: Date | null;\n failed_at: Date | null;\n cancelled_at: Date | null;\n last_error: unknown | null;\n payload: unknown;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface JobEventRow {\n id: string;\n generation_job_id: string;\n event_type: ObservationGenerationJobEventType;\n status_after: ObservationGenerationJobStatus;\n attempt: number;\n details: unknown;\n created_at: Date;\n}\n\nexport class PostgresObservationGenerationJobRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: {\n id?: string;\n projectId: string;\n teamId: string;\n sourceType: ObservationGenerationJobSourceType;\n sourceId: string;\n agentEventId?: string | null;\n serverSessionId?: string | null;\n jobType: string;\n status?: ObservationGenerationJobStatus;\n bullmqJobId?: string | null;\n maxAttempts?: number;\n payload?: JsonObject;\n }): Promise<PostgresObservationGenerationJob> {\n await this.validateSource(input);\n const sourceModel = normalizeSourceModel(input);\n const idempotencyKey = buildObservationGenerationJobIdempotencyKey(input);\n const row = await queryOne<JobRow>(\n this.client,\n `\n INSERT INTO observation_generation_jobs (\n id, project_id, team_id, agent_event_id, source_type, source_id,\n server_session_id, job_type, status, idempotency_key, bullmq_job_id,\n max_attempts, payload\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13::jsonb)\n ON CONFLICT (idempotency_key) DO UPDATE SET\n payload = observation_generation_jobs.payload || excluded.payload,\n updated_at = now()\n RETURNING *\n `,\n [\n input.id ?? newId(),\n input.projectId,\n input.teamId,\n sourceModel.agentEventId,\n input.sourceType,\n input.sourceId,\n sourceModel.serverSessionId,\n input.jobType,\n input.status ?? 'queued',\n idempotencyKey,\n input.bullmqJobId ?? null,\n input.maxAttempts ?? 3,\n JSON.stringify(input.payload ?? {})\n ]\n );\n return mapJobRow(row!);\n }\n\n async getByIdForScope(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresObservationGenerationJob | null> {\n const row = await queryOne<JobRow>(\n this.client,\n 'SELECT * FROM observation_generation_jobs WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.id, input.projectId, input.teamId]\n );\n return row ? mapJobRow(row) : null;\n }\n\n async transitionStatus(input: {\n id: string;\n projectId: string;\n teamId: string;\n status: ObservationGenerationJobStatus;\n lockedBy?: string | null;\n lastError?: JsonObject | null;\n nextAttemptAt?: Date | null;\n }): Promise<PostgresObservationGenerationJob | null> {\n const row = await queryOne<JobRow>(\n this.client,\n `\n UPDATE observation_generation_jobs\n SET\n status = $2,\n attempts = CASE WHEN $2 = 'processing' THEN attempts + 1 ELSE attempts END,\n locked_at = CASE WHEN $2 = 'processing' THEN now() ELSE NULL END,\n locked_by = CASE WHEN $2 = 'processing' THEN $3 ELSE NULL END,\n next_attempt_at = CASE WHEN $2 = 'queued' THEN $4::timestamptz ELSE NULL::timestamptz END,\n completed_at = CASE WHEN $2 = 'completed' THEN now() ELSE NULL END,\n failed_at = CASE WHEN $2 = 'failed' THEN now() ELSE NULL END,\n cancelled_at = CASE WHEN $2 = 'cancelled' THEN now() ELSE NULL END,\n last_error = $5::jsonb,\n updated_at = now()\n WHERE id = $1\n AND project_id = $6\n AND team_id = $7\n AND (\n (status = 'queued' AND $2 IN ('processing', 'failed', 'cancelled'))\n OR\n (status = 'processing' AND $2 IN ('queued', 'completed', 'failed', 'cancelled'))\n )\n AND ($2 <> 'processing' OR attempts < max_attempts)\n AND ($2 <> 'queued' OR attempts < max_attempts)\n RETURNING *\n `,\n [\n input.id,\n input.status,\n input.lockedBy ?? null,\n input.nextAttemptAt ?? null,\n input.lastError == null ? null : JSON.stringify(input.lastError),\n input.projectId,\n input.teamId\n ]\n );\n if (row) {\n return mapJobRow(row);\n }\n\n const current = await queryOne<JobRow>(\n this.client,\n 'SELECT * FROM observation_generation_jobs WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.id, input.projectId, input.teamId]\n );\n if (!current) {\n return null;\n }\n assertValidJobStatusTransition(mapJobRow(current), input.status);\n throw new Error('observation generation job status transition was not applied');\n }\n\n async listByStatusForScope(input: {\n status: ObservationGenerationJobStatus;\n projectId: string;\n teamId: string;\n limit?: number;\n }): Promise<PostgresObservationGenerationJob[]> {\n const result = await this.client.query<JobRow>(\n `\n SELECT * FROM observation_generation_jobs\n WHERE status = $1 AND project_id = $2 AND team_id = $3\n ORDER BY created_at ASC\n LIMIT $4\n `,\n [input.status, input.projectId, input.teamId, input.limit ?? 100]\n );\n return result.rows.map(mapJobRow);\n }\n\n private async validateSource(input: {\n projectId: string;\n teamId: string;\n sourceType: ObservationGenerationJobSourceType;\n sourceId: string;\n agentEventId?: string | null;\n serverSessionId?: string | null;\n }): Promise<void> {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n if (input.sourceType === 'agent_event') {\n const eventId = input.agentEventId ?? input.sourceId;\n const row = await queryOne<{ id: string; server_session_id: string | null }>(\n this.client,\n 'SELECT id, server_session_id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [eventId, input.projectId, input.teamId]\n );\n if (!row || input.sourceId !== eventId) {\n throw new Error('agent_event source_id must belong to project_id and team_id');\n }\n if (input.serverSessionId) {\n await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);\n if (row.server_session_id && row.server_session_id !== input.serverSessionId) {\n throw new Error('server_session_id must match the agent_event server_session_id');\n }\n }\n return;\n }\n\n if (input.sourceType === 'session_summary') {\n const sessionId = input.serverSessionId ?? input.sourceId;\n await assertSessionOwnership(this.client, sessionId, input.projectId, input.teamId);\n if (input.sourceId !== sessionId) {\n throw new Error('session_summary source_id must equal server_session_id');\n }\n return;\n }\n\n const observation = await queryOne<{ id: string }>(\n this.client,\n 'SELECT id FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.sourceId, input.projectId, input.teamId]\n );\n if (!observation) {\n throw new Error('observation_reindex source_id must belong to project_id and team_id');\n }\n if (input.serverSessionId) {\n await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);\n }\n }\n}\n\nexport class PostgresObservationGenerationJobEventsRepository {\n constructor(private client: PostgresQueryable) {}\n\n async append(input: {\n id?: string;\n generationJobId: string;\n projectId: string;\n teamId: string;\n eventType: ObservationGenerationJobEventType;\n statusAfter: ObservationGenerationJobStatus;\n attempt?: number;\n details?: JsonObject;\n }): Promise<PostgresObservationGenerationJobEvent> {\n const row = await queryOne<JobEventRow>(\n this.client,\n `\n INSERT INTO observation_generation_job_events (\n id, generation_job_id, event_type, status_after, attempt, details\n )\n SELECT $1, jobs.id, $4, $5, $6, $7::jsonb\n FROM observation_generation_jobs jobs\n WHERE jobs.id = $2\n AND jobs.project_id = $3\n AND jobs.team_id = $8\n RETURNING observation_generation_job_events.*\n `,\n [\n input.id ?? newId(),\n input.generationJobId,\n input.projectId,\n input.eventType,\n input.statusAfter,\n input.attempt ?? 0,\n JSON.stringify(input.details ?? {}),\n input.teamId\n ]\n );\n if (!row) {\n throw new Error('generation_job_id must belong to project_id and team_id');\n }\n return mapJobEventRow(row!);\n }\n\n async listByJobForScope(input: {\n generationJobId: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresObservationGenerationJobEvent[]> {\n const result = await this.client.query<JobEventRow>(\n `\n SELECT events.*\n FROM observation_generation_job_events events\n INNER JOIN observation_generation_jobs jobs ON jobs.id = events.generation_job_id\n WHERE events.generation_job_id = $1 AND jobs.project_id = $2 AND jobs.team_id = $3\n ORDER BY events.created_at ASC\n `,\n [input.generationJobId, input.projectId, input.teamId]\n );\n return result.rows.map(mapJobEventRow);\n }\n}\n\nexport function buildObservationGenerationJobIdempotencyKey(input: {\n teamId: string;\n projectId: string;\n sourceType: ObservationGenerationJobSourceType;\n sourceId: string;\n jobType: string;\n}): string {\n return `observation_generation_job:v1:${deterministicKey([\n input.teamId,\n input.projectId,\n input.sourceType,\n input.sourceId,\n input.jobType\n ])}`;\n}\n\nfunction normalizeSourceModel(input: {\n sourceType: ObservationGenerationJobSourceType;\n sourceId: string;\n agentEventId?: string | null;\n serverSessionId?: string | null;\n}): { agentEventId: string | null; serverSessionId: string | null } {\n if (input.sourceType === 'agent_event') {\n return { agentEventId: input.agentEventId ?? input.sourceId, serverSessionId: input.serverSessionId ?? null };\n }\n if (input.sourceType === 'session_summary') {\n return { agentEventId: null, serverSessionId: input.serverSessionId ?? input.sourceId };\n }\n return { agentEventId: null, serverSessionId: input.serverSessionId ?? null };\n}\n\nconst TERMINAL_JOB_STATUSES = new Set<ObservationGenerationJobStatus>(['completed', 'failed', 'cancelled']);\n\nconst ALLOWED_JOB_TRANSITIONS: Record<ObservationGenerationJobStatus, readonly ObservationGenerationJobStatus[]> = {\n queued: ['processing', 'failed', 'cancelled'],\n processing: ['queued', 'completed', 'failed', 'cancelled'],\n completed: [],\n failed: [],\n cancelled: []\n};\n\nfunction assertValidJobStatusTransition(\n current: PostgresObservationGenerationJob,\n nextStatus: ObservationGenerationJobStatus\n): void {\n if (TERMINAL_JOB_STATUSES.has(current.status)) {\n throw new Error(`cannot transition observation generation job from terminal status ${current.status}`);\n }\n\n if (!ALLOWED_JOB_TRANSITIONS[current.status].includes(nextStatus)) {\n throw new Error(`illegal observation generation job transition from ${current.status} to ${nextStatus}`);\n }\n\n if (nextStatus === 'processing' && current.attempts >= current.maxAttempts) {\n throw new Error('cannot process observation generation job after max_attempts is reached');\n }\n\n if (nextStatus === 'queued' && current.attempts >= current.maxAttempts) {\n throw new Error('cannot retry observation generation job after max_attempts is reached');\n }\n}\n\nfunction mapJobRow(row: JobRow): PostgresObservationGenerationJob {\n return {\n id: row.id,\n projectId: row.project_id,\n teamId: row.team_id,\n agentEventId: row.agent_event_id,\n sourceType: row.source_type,\n sourceId: row.source_id,\n serverSessionId: row.server_session_id,\n jobType: row.job_type,\n status: row.status,\n idempotencyKey: row.idempotency_key,\n bullmqJobId: row.bullmq_job_id,\n attempts: row.attempts,\n maxAttempts: row.max_attempts,\n nextAttemptAtEpoch: toDate(row.next_attempt_at)?.getTime() ?? null,\n lockedAtEpoch: toDate(row.locked_at)?.getTime() ?? null,\n lockedBy: row.locked_by,\n completedAtEpoch: toDate(row.completed_at)?.getTime() ?? null,\n failedAtEpoch: toDate(row.failed_at)?.getTime() ?? null,\n cancelledAtEpoch: toDate(row.cancelled_at)?.getTime() ?? null,\n lastError: row.last_error == null ? null : toJsonObject(row.last_error),\n payload: toJsonObject(row.payload),\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n\nfunction mapJobEventRow(row: JobEventRow): PostgresObservationGenerationJobEvent {\n return {\n id: row.id,\n generationJobId: row.generation_job_id,\n eventType: row.event_type,\n statusAfter: row.status_after,\n attempt: row.attempt,\n details: toJsonObject(row.details),\n createdAtEpoch: toEpoch(row.created_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, JsonValue, PostgresQueryable } from './utils.js';\nimport {\n assertProjectOwnership,\n assertSessionOwnership,\n canonicalJson,\n deterministicKey,\n newId,\n queryOne,\n toEpoch,\n toJsonObject\n} from './utils.js';\n\nexport type ObservationSourceType = 'agent_event' | 'session_summary' | 'observation_reindex' | 'manual';\n\nexport interface PostgresObservation {\n id: string;\n projectId: string;\n teamId: string;\n serverSessionId: string | null;\n kind: string;\n content: string;\n generationKey: string | null;\n metadata: JsonObject;\n embedding: JsonValue | null;\n createdByJobId: string | null;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\nexport interface PostgresObservationSource {\n id: string;\n observationId: string;\n agentEventId: string | null;\n generationJobId: string | null;\n sourceType: ObservationSourceType;\n sourceId: string;\n metadata: JsonObject;\n createdAtEpoch: number;\n}\n\ninterface ObservationRow {\n id: string;\n project_id: string;\n team_id: string;\n server_session_id: string | null;\n kind: string;\n content: string;\n generation_key: string | null;\n metadata: unknown;\n embedding: unknown | null;\n created_by_job_id: string | null;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface ObservationSourceRow {\n id: string;\n observation_id: string;\n agent_event_id: string | null;\n generation_job_id: string | null;\n source_type: ObservationSourceType;\n source_id: string;\n metadata: unknown;\n created_at: Date;\n}\n\nexport class PostgresObservationRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: {\n id?: string;\n projectId: string;\n teamId: string;\n serverSessionId?: string | null;\n kind?: string;\n content: string;\n generationKey?: string | null;\n metadata?: JsonObject;\n embedding?: JsonValue | null;\n createdByJobId?: string | null;\n }): Promise<PostgresObservation> {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n if (input.serverSessionId) {\n await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);\n }\n if (input.createdByJobId) {\n await assertJobOwnership(this.client, input.createdByJobId, input.projectId, input.teamId);\n }\n\n const row = await queryOne<ObservationRow>(\n this.client,\n `\n INSERT INTO observations (\n id, project_id, team_id, server_session_id, kind, content,\n generation_key, metadata, embedding, created_by_job_id\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9::jsonb, $10)\n ON CONFLICT (team_id, project_id, generation_key) WHERE generation_key IS NOT NULL DO UPDATE SET\n updated_at = observations.updated_at\n RETURNING *\n `,\n [\n input.id ?? newId(),\n input.projectId,\n input.teamId,\n input.serverSessionId ?? null,\n input.kind ?? 'observation',\n input.content,\n input.generationKey ?? null,\n JSON.stringify(input.metadata ?? {}),\n input.embedding == null ? null : JSON.stringify(input.embedding),\n input.createdByJobId ?? null\n ]\n );\n return mapObservationRow(row!);\n }\n\n async getByIdForScope(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresObservation | null> {\n const row = await queryOne<ObservationRow>(\n this.client,\n 'SELECT * FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.id, input.projectId, input.teamId]\n );\n return row ? mapObservationRow(row) : null;\n }\n\n async listByProject(input: {\n projectId: string;\n teamId: string;\n serverSessionId?: string | null;\n limit?: number;\n }): Promise<PostgresObservation[]> {\n const result = await this.client.query<ObservationRow>(\n `\n SELECT * FROM observations\n WHERE project_id = $1\n AND team_id = $2\n AND ($3::text IS NULL OR server_session_id = $3)\n ORDER BY created_at DESC\n LIMIT $4\n `,\n [input.projectId, input.teamId, input.serverSessionId ?? null, input.limit ?? 100]\n );\n return result.rows.map(mapObservationRow);\n }\n\n async search(input: {\n projectId: string;\n teamId: string;\n query: string;\n limit?: number;\n }): Promise<PostgresObservation[]> {\n const result = await this.client.query<ObservationRow>(\n `\n SELECT * FROM observations\n WHERE project_id = $1\n AND team_id = $2\n AND content_search @@ websearch_to_tsquery('english', $3)\n ORDER BY ts_rank(content_search, websearch_to_tsquery('english', $3)) DESC, updated_at DESC\n LIMIT $4\n `,\n [input.projectId, input.teamId, input.query, input.limit ?? 20]\n );\n return result.rows.map(mapObservationRow);\n }\n}\n\nexport class PostgresObservationSourcesRepository {\n constructor(private client: PostgresQueryable) {}\n\n async addSource(input: {\n id?: string;\n observationId: string;\n projectId: string;\n teamId: string;\n sourceType: ObservationSourceType;\n sourceId: string;\n agentEventId?: string | null;\n generationJobId?: string | null;\n metadata?: JsonObject;\n }): Promise<PostgresObservationSource> {\n const observation = await queryOne<{ id: string }>(\n this.client,\n 'SELECT id FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.observationId, input.projectId, input.teamId]\n );\n if (!observation) {\n throw new Error('observation_id does not exist');\n }\n\n const agentEventId = input.sourceType === 'agent_event'\n ? input.agentEventId ?? input.sourceId\n : null;\n\n if (input.sourceType === 'agent_event') {\n if (agentEventId !== input.sourceId) {\n throw new Error('agent_event source_id must equal agent_event_id');\n }\n await assertAgentEventOwnership(this.client, input.sourceId, input.projectId, input.teamId);\n } else if (input.sourceType === 'session_summary' && !input.generationJobId) {\n await assertSessionOwnership(this.client, input.sourceId, input.projectId, input.teamId);\n } else if (input.sourceType === 'observation_reindex' && !input.generationJobId) {\n await assertObservationOwnership(this.client, input.sourceId, input.projectId, input.teamId);\n }\n if (input.generationJobId) {\n await assertGenerationJobMatchesSource(this.client, {\n generationJobId: input.generationJobId,\n projectId: input.projectId,\n teamId: input.teamId,\n sourceType: input.sourceType,\n sourceId: input.sourceId,\n agentEventId\n });\n }\n\n const row = await queryOne<ObservationSourceRow>(\n this.client,\n `\n INSERT INTO observation_sources (\n id, observation_id, agent_event_id, generation_job_id,\n source_type, source_id, metadata\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)\n ON CONFLICT (observation_id, source_type, source_id) DO UPDATE SET\n metadata = observation_sources.metadata || excluded.metadata\n RETURNING *\n `,\n [\n input.id ?? newId(),\n input.observationId,\n agentEventId,\n input.generationJobId ?? null,\n input.sourceType,\n input.sourceId,\n JSON.stringify(input.metadata ?? {})\n ]\n );\n return mapObservationSourceRow(row!);\n }\n\n async listByObservationForScope(input: {\n observationId: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresObservationSource[]> {\n const result = await this.client.query<ObservationSourceRow>(\n `\n SELECT observation_sources.*\n FROM observation_sources\n INNER JOIN observations\n ON observations.id = observation_sources.observation_id\n WHERE observation_sources.observation_id = $1\n AND observations.project_id = $2\n AND observations.team_id = $3\n ORDER BY observation_sources.created_at ASC\n `,\n [input.observationId, input.projectId, input.teamId]\n );\n return result.rows.map(mapObservationSourceRow);\n }\n}\n\nexport function buildObservationGenerationKey(input: {\n generationJobId: string;\n parsedObservationIndex: number;\n content: string;\n}): string {\n return `generation:v1:${input.generationJobId}:${input.parsedObservationIndex}:${deterministicKey([\n canonicalJson(input.content.trim())\n ])}`;\n}\n\nasync function assertJobOwnership(\n client: PostgresQueryable,\n generationJobId: string,\n projectId: string,\n teamId: string\n): Promise<void> {\n const row = await queryOne<{ id: string }>(\n client,\n 'SELECT id FROM observation_generation_jobs WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [generationJobId, projectId, teamId]\n );\n if (!row) {\n throw new Error('generation_job_id must belong to project_id and team_id');\n }\n}\n\nasync function assertGenerationJobMatchesSource(\n client: PostgresQueryable,\n input: {\n generationJobId: string;\n projectId: string;\n teamId: string;\n sourceType: ObservationSourceType;\n sourceId: string;\n agentEventId: string | null;\n }\n): Promise<void> {\n if (input.sourceType === 'manual') {\n throw new Error('manual observation sources cannot be linked to a generation_job_id');\n }\n\n const row = await queryOne<{\n id: string;\n source_type: string;\n source_id: string;\n agent_event_id: string | null;\n }>(\n client,\n `\n SELECT id, source_type, source_id, agent_event_id\n FROM observation_generation_jobs\n WHERE id = $1 AND project_id = $2 AND team_id = $3\n `,\n [input.generationJobId, input.projectId, input.teamId]\n );\n if (!row) {\n throw new Error('generation_job_id must belong to project_id and team_id');\n }\n if (row.source_type !== input.sourceType || row.source_id !== input.sourceId) {\n throw new Error('generation_job_id source model must match observation source');\n }\n if (input.sourceType === 'agent_event' && row.agent_event_id !== input.agentEventId) {\n throw new Error('generation_job_id agent_event_id must match observation source');\n }\n}\n\nasync function assertAgentEventOwnership(\n client: PostgresQueryable,\n agentEventId: string,\n projectId: string,\n teamId: string\n): Promise<void> {\n const row = await queryOne<{ id: string }>(\n client,\n 'SELECT id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [agentEventId, projectId, teamId]\n );\n if (!row) {\n throw new Error('agent_event_id must belong to project_id and team_id');\n }\n}\n\nasync function assertObservationOwnership(\n client: PostgresQueryable,\n observationId: string,\n projectId: string,\n teamId: string\n): Promise<void> {\n const row = await queryOne<{ id: string }>(\n client,\n 'SELECT id FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [observationId, projectId, teamId]\n );\n if (!row) {\n throw new Error('observation_reindex source_id must belong to project_id and team_id');\n }\n}\n\nfunction mapObservationRow(row: ObservationRow): PostgresObservation {\n return {\n id: row.id,\n projectId: row.project_id,\n teamId: row.team_id,\n serverSessionId: row.server_session_id,\n kind: row.kind,\n content: row.content,\n generationKey: row.generation_key,\n metadata: toJsonObject(row.metadata),\n embedding: row.embedding,\n createdByJobId: row.created_by_job_id,\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n\nfunction mapObservationSourceRow(row: ObservationSourceRow): PostgresObservationSource {\n return {\n id: row.id,\n observationId: row.observation_id,\n agentEventId: row.agent_event_id,\n generationJobId: row.generation_job_id,\n sourceType: row.source_type,\n sourceId: row.source_id,\n metadata: toJsonObject(row.metadata),\n createdAtEpoch: toEpoch(row.created_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, PostgresQueryable } from './utils.js';\nimport { newId, queryOne, toEpoch, toJsonObject } from './utils.js';\n\nexport interface PostgresProject {\n id: string;\n teamId: string;\n name: string;\n metadata: JsonObject;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\ninterface ProjectRow {\n id: string;\n team_id: string;\n name: string;\n metadata: unknown;\n created_at: Date;\n updated_at: Date;\n}\n\nexport class PostgresProjectsRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: {\n id?: string;\n teamId: string;\n name: string;\n metadata?: JsonObject;\n }): Promise<PostgresProject> {\n const id = input.id ?? newId();\n const row = await queryOne<ProjectRow>(\n this.client,\n `\n INSERT INTO projects (id, team_id, name, metadata)\n VALUES ($1, $2, $3, $4::jsonb)\n RETURNING *\n `,\n [id, input.teamId, input.name, JSON.stringify(input.metadata ?? {})]\n );\n return mapProjectRow(row!);\n }\n\n async getByIdForTeam(id: string, teamId: string): Promise<PostgresProject | null> {\n const row = await queryOne<ProjectRow>(\n this.client,\n 'SELECT * FROM projects WHERE id = $1 AND team_id = $2',\n [id, teamId]\n );\n return row ? mapProjectRow(row) : null;\n }\n}\n\nfunction mapProjectRow(row: ProjectRow): PostgresProject {\n return {\n id: row.id,\n teamId: row.team_id,\n name: row.name,\n metadata: toJsonObject(row.metadata),\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, PostgresQueryable } from './utils.js';\nimport { assertProjectOwnership, deterministicKey, newId, queryOne, toDate, toEpoch, toJsonObject } from './utils.js';\nimport type { PostgresAgentEvent } from './agent-events.js';\n\nexport interface PostgresServerSession {\n id: string;\n projectId: string;\n teamId: string;\n externalSessionId: string | null;\n idempotencyKey: string | null;\n contentSessionId: string | null;\n agentId: string | null;\n agentType: string | null;\n platformSource: string | null;\n generationStatus: string;\n metadata: JsonObject;\n startedAtEpoch: number;\n endedAtEpoch: number | null;\n lastGeneratedAtEpoch: number | null;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\ninterface ServerSessionRow {\n id: string;\n project_id: string;\n team_id: string;\n external_session_id: string | null;\n idempotency_key: string | null;\n content_session_id: string | null;\n agent_id: string | null;\n agent_type: string | null;\n platform_source: string | null;\n generation_status: string;\n metadata: unknown;\n started_at: Date;\n ended_at: Date | null;\n last_generated_at: Date | null;\n created_at: Date;\n updated_at: Date;\n}\n\nexport class PostgresServerSessionsRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: {\n id?: string;\n projectId: string;\n teamId: string;\n externalSessionId?: string | null;\n contentSessionId?: string | null;\n agentId?: string | null;\n agentType?: string | null;\n platformSource?: string | null;\n generationStatus?: string;\n metadata?: JsonObject;\n }): Promise<PostgresServerSession> {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n const id = input.id ?? newId();\n const idempotencyKey = buildServerSessionIdempotencyKey(input);\n const row = await queryOne<ServerSessionRow>(\n this.client,\n `\n INSERT INTO server_sessions (\n id, project_id, team_id, external_session_id, idempotency_key, content_session_id,\n agent_id, agent_type, platform_source, generation_status, metadata\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11::jsonb)\n ON CONFLICT (project_id, idempotency_key) WHERE idempotency_key IS NOT NULL DO UPDATE SET\n external_session_id = excluded.external_session_id,\n content_session_id = excluded.content_session_id,\n agent_id = excluded.agent_id,\n agent_type = excluded.agent_type,\n platform_source = excluded.platform_source,\n generation_status = excluded.generation_status,\n metadata = excluded.metadata,\n updated_at = now()\n RETURNING *\n `,\n [\n id,\n input.projectId,\n input.teamId,\n input.externalSessionId ?? null,\n idempotencyKey,\n input.contentSessionId ?? null,\n input.agentId ?? null,\n input.agentType ?? null,\n input.platformSource ?? null,\n input.generationStatus ?? 'idle',\n JSON.stringify(input.metadata ?? {})\n ]\n );\n return mapServerSessionRow(row!);\n }\n\n async getByIdForScope(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresServerSession | null> {\n const row = await queryOne<ServerSessionRow>(\n this.client,\n 'SELECT * FROM server_sessions WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.id, input.projectId, input.teamId]\n );\n return row ? mapServerSessionRow(row) : null;\n }\n\n async listByProject(projectId: string, teamId: string): Promise<PostgresServerSession[]> {\n const result = await this.client.query<ServerSessionRow>(\n `\n SELECT * FROM server_sessions\n WHERE project_id = $1 AND team_id = $2\n ORDER BY started_at DESC\n `,\n [projectId, teamId]\n );\n return result.rows.map(mapServerSessionRow);\n }\n\n async findByExternalIdForScope(input: {\n externalSessionId: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresServerSession | null> {\n const row = await queryOne<ServerSessionRow>(\n this.client,\n `\n SELECT * FROM server_sessions\n WHERE external_session_id = $1 AND project_id = $2 AND team_id = $3\n `,\n [input.externalSessionId, input.projectId, input.teamId]\n );\n return row ? mapServerSessionRow(row) : null;\n }\n\n // Resolve only the server_session id for a content session. Used by the\n // /v1/events ingest linkage path, which needs nothing but the id, so we select\n // a single column to keep this hot-path query light.\n async findIdByContentSessionId(input: {\n contentSessionId: string;\n projectId: string;\n teamId: string;\n }): Promise<string | null> {\n const row = await queryOne<{ id: string }>(\n this.client,\n `\n SELECT id FROM server_sessions\n WHERE content_session_id = $1 AND project_id = $2 AND team_id = $3\n ORDER BY started_at DESC\n LIMIT 1\n `,\n [input.contentSessionId, input.projectId, input.teamId]\n );\n return row ? row.id : null;\n }\n\n /**\n * End a server session by setting `ended_at = now()` if not already set.\n * Idempotent: if `ended_at` is already populated, returns the row unchanged.\n * Returns null if no row matches the (id, project_id, team_id) tuple.\n */\n async endSession(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresServerSession | null> {\n const updated = await queryOne<ServerSessionRow>(\n this.client,\n `\n UPDATE server_sessions\n SET ended_at = COALESCE(ended_at, now()),\n updated_at = CASE WHEN ended_at IS NULL THEN now() ELSE updated_at END\n WHERE id = $1 AND project_id = $2 AND team_id = $3\n RETURNING *\n `,\n [input.id, input.projectId, input.teamId]\n );\n return updated ? mapServerSessionRow(updated) : null;\n }\n\n async markGenerationStarted(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresServerSession | null> {\n const updated = await queryOne<ServerSessionRow>(\n this.client,\n `\n UPDATE server_sessions\n SET generation_status = 'processing', updated_at = now()\n WHERE id = $1 AND project_id = $2 AND team_id = $3\n RETURNING *\n `,\n [input.id, input.projectId, input.teamId]\n );\n return updated ? mapServerSessionRow(updated) : null;\n }\n\n async markGenerationCompleted(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresServerSession | null> {\n const updated = await queryOne<ServerSessionRow>(\n this.client,\n `\n UPDATE server_sessions\n SET generation_status = 'completed',\n last_generated_at = now(),\n updated_at = now()\n WHERE id = $1 AND project_id = $2 AND team_id = $3\n RETURNING *\n `,\n [input.id, input.projectId, input.teamId]\n );\n return updated ? mapServerSessionRow(updated) : null;\n }\n\n async markGenerationFailed(input: {\n id: string;\n projectId: string;\n teamId: string;\n error?: string | null;\n }): Promise<PostgresServerSession | null> {\n const updated = await queryOne<ServerSessionRow>(\n this.client,\n `\n UPDATE server_sessions\n SET generation_status = 'failed',\n metadata = jsonb_set(\n COALESCE(metadata, '{}'::jsonb),\n '{lastGenerationError}',\n COALESCE(to_jsonb($4::text), 'null'::jsonb),\n true\n ),\n updated_at = now()\n WHERE id = $1 AND project_id = $2 AND team_id = $3\n RETURNING *\n `,\n [input.id, input.projectId, input.teamId, input.error ?? null]\n );\n return updated ? mapServerSessionRow(updated) : null;\n }\n\n /**\n * List events tied to this server_session that do NOT yet have a completed\n * observation_generation_jobs row. Tenant-scoped: rows are filtered by\n * (project_id, team_id) before any join.\n */\n async listUnprocessedEvents(input: {\n serverSessionId: string;\n projectId: string;\n teamId: string;\n limit?: number;\n }): Promise<PostgresAgentEvent[]> {\n const limit = input.limit ?? 500;\n const result = await this.client.query<UnprocessedEventRow>(\n `\n SELECT e.*\n FROM agent_events e\n WHERE e.server_session_id = $1\n AND e.project_id = $2\n AND e.team_id = $3\n AND NOT EXISTS (\n SELECT 1 FROM observation_generation_jobs j\n WHERE j.agent_event_id = e.id\n AND j.project_id = e.project_id\n AND j.team_id = e.team_id\n AND j.source_type = 'agent_event'\n AND j.status = 'completed'\n )\n ORDER BY e.occurred_at ASC\n LIMIT $4\n `,\n [input.serverSessionId, input.projectId, input.teamId, limit]\n );\n return result.rows.map(mapUnprocessedEventRow);\n }\n}\n\ninterface UnprocessedEventRow {\n id: string;\n project_id: string;\n team_id: string;\n server_session_id: string | null;\n source_adapter: string;\n source_event_id: string | null;\n idempotency_key: string;\n event_type: string;\n platform_source: string | null;\n payload: unknown;\n metadata: unknown;\n occurred_at: Date;\n received_at: Date;\n created_at: Date;\n}\n\nfunction mapUnprocessedEventRow(row: UnprocessedEventRow): PostgresAgentEvent {\n return {\n id: row.id,\n projectId: row.project_id,\n teamId: row.team_id,\n serverSessionId: row.server_session_id,\n sourceAdapter: row.source_adapter,\n sourceEventId: row.source_event_id,\n idempotencyKey: row.idempotency_key,\n eventType: row.event_type,\n platformSource: row.platform_source,\n payload: toJsonObject(row.payload),\n metadata: toJsonObject(row.metadata),\n occurredAtEpoch: row.occurred_at.getTime(),\n receivedAtEpoch: row.received_at.getTime(),\n createdAtEpoch: row.created_at.getTime()\n };\n}\n\nexport function buildServerSessionIdempotencyKey(input: {\n projectId: string;\n teamId: string;\n externalSessionId?: string | null;\n contentSessionId?: string | null;\n agentId?: string | null;\n agentType?: string | null;\n platformSource?: string | null;\n}): string | null {\n if (input.externalSessionId) {\n return `server_session:v1:${deterministicKey([\n input.teamId,\n input.projectId,\n 'external',\n input.externalSessionId\n ])}`;\n }\n\n if (input.contentSessionId) {\n return `server_session:v1:${deterministicKey([\n input.teamId,\n input.projectId,\n 'content',\n input.platformSource ?? null,\n input.agentId ?? null,\n input.contentSessionId\n ])}`;\n }\n\n if (input.agentId && input.platformSource) {\n return `server_session:v1:${deterministicKey([\n input.teamId,\n input.projectId,\n 'agent',\n input.platformSource,\n input.agentId,\n input.agentType ?? null\n ])}`;\n }\n\n return null;\n}\n\nfunction mapServerSessionRow(row: ServerSessionRow): PostgresServerSession {\n return {\n id: row.id,\n projectId: row.project_id,\n teamId: row.team_id,\n externalSessionId: row.external_session_id,\n idempotencyKey: row.idempotency_key,\n contentSessionId: row.content_session_id,\n agentId: row.agent_id,\n agentType: row.agent_type,\n platformSource: row.platform_source,\n generationStatus: row.generation_status,\n metadata: toJsonObject(row.metadata),\n startedAtEpoch: toEpoch(row.started_at),\n endedAtEpoch: toDate(row.ended_at)?.getTime() ?? null,\n lastGeneratedAtEpoch: toDate(row.last_generated_at)?.getTime() ?? null,\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { PostgresQueryable, JsonObject } from './utils.js';\nimport { newId, queryOne, toEpoch, toJsonObject } from './utils.js';\n\nexport type PostgresTeamRole = 'owner' | 'admin' | 'member' | 'viewer';\n\nexport interface PostgresTeam {\n id: string;\n name: string;\n metadata: JsonObject;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\nexport interface PostgresTeamMember {\n teamId: string;\n userId: string;\n role: PostgresTeamRole;\n metadata: JsonObject;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\ninterface TeamRow {\n id: string;\n name: string;\n metadata: unknown;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface TeamMemberRow {\n team_id: string;\n user_id: string;\n role: PostgresTeamRole;\n metadata: unknown;\n created_at: Date;\n updated_at: Date;\n}\n\nexport class PostgresTeamsRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: { id?: string; name: string; metadata?: JsonObject }): Promise<PostgresTeam> {\n const id = input.id ?? newId();\n const row = await queryOne<TeamRow>(\n this.client,\n `\n INSERT INTO teams (id, name, metadata)\n VALUES ($1, $2, $3::jsonb)\n RETURNING *\n `,\n [id, input.name, JSON.stringify(input.metadata ?? {})]\n );\n return mapTeamRow(row!);\n }\n\n async addMember(input: {\n teamId: string;\n userId: string;\n role: PostgresTeamRole;\n metadata?: JsonObject;\n }): Promise<PostgresTeamMember> {\n const row = await queryOne<TeamMemberRow>(\n this.client,\n `\n INSERT INTO team_members (team_id, user_id, role, metadata)\n VALUES ($1, $2, $3, $4::jsonb)\n ON CONFLICT (team_id, user_id) DO UPDATE SET\n role = excluded.role,\n metadata = excluded.metadata,\n updated_at = now()\n RETURNING *\n `,\n [input.teamId, input.userId, input.role, JSON.stringify(input.metadata ?? {})]\n );\n return mapTeamMemberRow(row!);\n }\n\n async getByIdForUser(input: {\n id: string;\n userId: string;\n }): Promise<PostgresTeam | null> {\n const row = await queryOne<TeamRow>(\n this.client,\n `\n SELECT teams.*\n FROM teams\n INNER JOIN team_members ON team_members.team_id = teams.id\n WHERE teams.id = $1 AND team_members.user_id = $2\n `,\n [input.id, input.userId]\n );\n return row ? mapTeamRow(row) : null;\n }\n}\n\nfunction mapTeamRow(row: TeamRow): PostgresTeam {\n return {\n id: row.id,\n name: row.name,\n metadata: toJsonObject(row.metadata),\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n\nfunction mapTeamMemberRow(row: TeamMemberRow): PostgresTeamMember {\n return {\n teamId: row.team_id,\n userId: row.user_id,\n role: row.role,\n metadata: toJsonObject(row.metadata),\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { PostgresQueryable } from './utils.js';\nimport { PostgresAgentEventsRepository } from './agent-events.js';\nimport { PostgresAuthRepository } from './auth.js';\nimport {\n PostgresObservationGenerationJobEventsRepository,\n PostgresObservationGenerationJobRepository\n} from './generation-jobs.js';\nimport { PostgresObservationRepository, PostgresObservationSourcesRepository } from './observations.js';\nimport { PostgresProjectsRepository } from './projects.js';\nimport { PostgresServerSessionsRepository } from './server-sessions.js';\nimport { PostgresTeamsRepository } from './teams.js';\n\nexport * from './agent-events.js';\nexport * from './auth.js';\nexport * from './config.js';\nexport * from './generation-jobs.js';\nexport * from './observations.js';\nexport * from './pool.js';\nexport * from './projects.js';\nexport * from './schema.js';\nexport * from './server-sessions.js';\nexport * from './teams.js';\nexport type * from './utils.js';\n\nexport interface PostgresStorageRepositories {\n teams: PostgresTeamsRepository;\n projects: PostgresProjectsRepository;\n auth: PostgresAuthRepository;\n sessions: PostgresServerSessionsRepository;\n agentEvents: PostgresAgentEventsRepository;\n observations: PostgresObservationRepository;\n observationSources: PostgresObservationSourcesRepository;\n observationGenerationJobs: PostgresObservationGenerationJobRepository;\n observationGenerationJobEvents: PostgresObservationGenerationJobEventsRepository;\n}\n\nexport function createPostgresStorageRepositories(client: PostgresQueryable): PostgresStorageRepositories {\n return {\n teams: new PostgresTeamsRepository(client),\n projects: new PostgresProjectsRepository(client),\n auth: new PostgresAuthRepository(client),\n sessions: new PostgresServerSessionsRepository(client),\n agentEvents: new PostgresAgentEventsRepository(client),\n observations: new PostgresObservationRepository(client),\n observationSources: new PostgresObservationSourcesRepository(client),\n observationGenerationJobs: new PostgresObservationGenerationJobRepository(client),\n observationGenerationJobEvents: new PostgresObservationGenerationJobEventsRepository(client)\n };\n}\n","\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport { execFile, execSync, type ChildProcess } from 'child_process';\nimport { promisify } from 'util';\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs';\nimport { logger } from '../../utils/logger.js';\nimport { SettingsDefaultsManager } from '../../shared/SettingsDefaultsManager.js';\nimport { USER_SETTINGS_PATH, paths } from '../../shared/paths.js';\nimport { sanitizeEnv } from '../../supervisor/env-sanitizer.js';\nimport { getSupervisor } from '../../supervisor/index.js';\n\nconst execFileAsync = promisify(execFile);\n\nconst CHROMA_MCP_CLIENT_NAME = 'claude-mem-chroma';\nconst CHROMA_MCP_CLIENT_VERSION = '1.0.0';\nconst MCP_CONNECTION_TIMEOUT_MS = 30_000;\nconst RECONNECT_BACKOFF_MS = 10_000;\nconst DEFAULT_CHROMA_DATA_DIR = paths.chroma();\nconst CHROMA_SUPERVISOR_ID = 'chroma-mcp';\n\nconst CHROMA_MCP_PINNED_VERSION = '0.2.6';\n\n// Override transitive dep resolutions for chroma-mcp 0.2.6 (issue #2371).\n//\n// Why onnxruntime>=1.20: the shipped all-MiniLM-L6-v2 model has pytorch-2.0\n// IR. Older onnxruntime versions can't parse it and fail every embedding\n// add with `[ONNXRuntimeError] : 7 : INVALID_PROTOBUF`. uv may otherwise\n// resolve to a too-old onnxruntime on macOS arm64 / Python 3.13 depending\n// on cache state, so we force a floor.\n//\n// Why protobuf<7: protobuf 7.x's stricter generated-file check rejects\n// opentelemetry's _pb2 stubs (generated with protoc <3.19), throwing\n// `TypeError: Descriptors cannot be created directly` at chromadb import.\n// Capping below 7 lands on protobuf 6.x which opentelemetry tolerates.\n//\n// These pins are runtime-only (uvx --with) so we don't have to fork\n// chroma-mcp upstream — they apply only to claude-mem's spawned subprocess.\nconst CHROMA_MCP_DEP_OVERRIDES: ReadonlyArray<string> = [\n 'onnxruntime>=1.20',\n 'protobuf<7',\n];\n\n// Issue #2696 (revised): chroma-mcp is now spawned by invoking uvx DIRECTLY on\n// every platform — see ChromaMcpManager.resolveUvxCommand(). The previous\n// `cmd.exe /c uvx ...` path, and the cmd.exe metacharacter-quoting helper that\n// went with it, were removed: even with the dep-override specs wrapped in double\n// quotes, Node's child_process arg-quoting for cmd.exe re-mangled the `>`/`<` in\n// `onnxruntime>=1.20` / `protobuf<7`, so cmd.exe parsed them as redirection and\n// died with \"The directory name is invalid\" in ~10ms — killing semantic search.\n\nexport class ChromaMcpManager {\n private static instance: ChromaMcpManager | null = null;\n private client: Client | null = null;\n private transport: StdioClientTransport | null = null;\n private connected: boolean = false;\n private lastConnectionFailureTimestamp: number = 0;\n private connecting: Promise<void> | null = null;\n\n private constructor() {}\n\n static getInstance(): ChromaMcpManager {\n if (!ChromaMcpManager.instance) {\n ChromaMcpManager.instance = new ChromaMcpManager();\n }\n return ChromaMcpManager.instance;\n }\n\n private async ensureConnected(): Promise<void> {\n if (this.connected && this.client) {\n return;\n }\n\n const timeSinceLastFailure = Date.now() - this.lastConnectionFailureTimestamp;\n if (this.lastConnectionFailureTimestamp > 0 && timeSinceLastFailure < RECONNECT_BACKOFF_MS) {\n throw new Error(`chroma-mcp connection in backoff (${Math.ceil((RECONNECT_BACKOFF_MS - timeSinceLastFailure) / 1000)}s remaining)`);\n }\n\n if (this.connecting) {\n await this.connecting;\n return;\n }\n\n this.connecting = this.connectInternal();\n try {\n await this.connecting;\n } catch (error) {\n this.lastConnectionFailureTimestamp = Date.now();\n if (error instanceof Error) {\n logger.error('CHROMA_MCP', 'Connection attempt failed', {}, error);\n } else {\n logger.error('CHROMA_MCP', 'Connection attempt failed with non-Error value', { error: String(error) });\n }\n throw error;\n } finally {\n this.connecting = null;\n }\n }\n\n private async connectInternal(): Promise<void> {\n // Singleton invariant (#2313): kill any pre-existing chroma-mcp subprocess\n // tree before spawning a new one. The MCP SDK's transport.close() only\n // signals the direct child (uvx); on Linux the grandchildren (uv, python,\n // chroma-mcp) get re-parented to init and survive, accumulating 20+\n // instances per session if reconnects fire repeatedly. Reuse the same\n // tree-kill primitive used by stop() so reconnect can never leave\n // orphans behind.\n await this.disposeCurrentSubprocess();\n\n const commandArgs = this.buildCommandArgs();\n const spawnEnvironment = this.getSpawnEnv();\n getSupervisor().assertCanSpawn('chroma mcp');\n\n // Spawn uvx DIRECTLY (no `cmd.exe /c` shell). On Windows, routing through\n // cmd.exe makes it parse the `>`/`<` in the dep-override specs as shell\n // redirection before uvx sees them; a shell-less spawn passes them literally.\n // resolveUvxCommand returns the absolute uvx.exe path on Windows (Node won't\n // PATHEXT-resolve a bare `uvx`) and bare `uvx` elsewhere (#2696).\n const uvxSpawnCommand = ChromaMcpManager.resolveUvxCommand();\n const uvxSpawnArgs = commandArgs;\n\n logger.info('CHROMA_MCP', 'Connecting to chroma-mcp via MCP stdio', {\n command: uvxSpawnCommand,\n args: uvxSpawnArgs.join(' ')\n });\n\n this.transport = new StdioClientTransport({\n command: uvxSpawnCommand,\n args: uvxSpawnArgs,\n env: spawnEnvironment,\n cwd: os.homedir(),\n stderr: 'pipe'\n });\n\n this.client = new Client(\n { name: CHROMA_MCP_CLIENT_NAME, version: CHROMA_MCP_CLIENT_VERSION },\n { capabilities: {} }\n );\n\n const mcpConnectionPromise = this.client.connect(this.transport);\n let timeoutId: ReturnType<typeof setTimeout>;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new Error(`MCP connection to chroma-mcp timed out after ${MCP_CONNECTION_TIMEOUT_MS}ms`)),\n MCP_CONNECTION_TIMEOUT_MS\n );\n });\n\n try {\n await Promise.race([mcpConnectionPromise, timeoutPromise]);\n } catch (connectionError) {\n clearTimeout(timeoutId!);\n logger.warn('CHROMA_MCP', 'Connection failed, killing subprocess tree to prevent zombie', {\n error: connectionError instanceof Error ? connectionError.message : String(connectionError)\n });\n // Tree-kill (not just transport.close) so failed-connect descendants\n // can't survive on Linux (#2313).\n await this.disposeCurrentSubprocess();\n throw connectionError;\n }\n clearTimeout(timeoutId!);\n\n this.connected = true;\n this.registerManagedProcess();\n\n logger.info('CHROMA_MCP', 'Connected to chroma-mcp successfully');\n\n const currentTransport = this.transport;\n const currentTrackedPid = (this.transport as unknown as { _process?: ChildProcess })._process?.pid;\n this.transport.onclose = () => {\n if (this.transport !== currentTransport) {\n logger.debug('CHROMA_MCP', 'Ignoring stale onclose from previous transport');\n return;\n }\n logger.warn('CHROMA_MCP', 'chroma-mcp subprocess closed unexpectedly, applying reconnect backoff');\n this.connected = false;\n getSupervisor().unregisterProcess(CHROMA_SUPERVISOR_ID);\n this.client = null;\n this.transport = null;\n this.lastConnectionFailureTimestamp = Date.now();\n\n // Direct child (uvx) emitted close, but on Linux the grandchildren\n // (uv/python/chroma-mcp) often outlive their parent because MCP SDK\n // does not use process groups. Sweep the descendant tree using the\n // captured PID — best-effort; pgrep returns nothing if everything\n // already exited (#2313).\n if (currentTrackedPid) {\n ChromaMcpManager.killProcessTree(currentTrackedPid).catch((error) => {\n logger.debug('CHROMA_MCP', 'Background tree-kill after onclose finished (best-effort)', {\n pid: currentTrackedPid,\n error: error instanceof Error ? error.message : String(error)\n });\n });\n }\n };\n }\n\n private buildCommandArgs(): string[] {\n const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);\n const chromaMode = settings.CLAUDE_MEM_CHROMA_MODE || 'local';\n const pythonVersion = process.env.CLAUDE_MEM_PYTHON_VERSION || settings.CLAUDE_MEM_PYTHON_VERSION || '3.13';\n\n const depOverrideFlags = CHROMA_MCP_DEP_OVERRIDES.flatMap(spec => ['--with', spec]);\n\n if (chromaMode === 'remote') {\n const chromaHost = settings.CLAUDE_MEM_CHROMA_HOST || '127.0.0.1';\n const chromaPort = settings.CLAUDE_MEM_CHROMA_PORT || '8000';\n const chromaSsl = settings.CLAUDE_MEM_CHROMA_SSL === 'true';\n const chromaTenant = settings.CLAUDE_MEM_CHROMA_TENANT || 'default_tenant';\n const chromaDatabase = settings.CLAUDE_MEM_CHROMA_DATABASE || 'default_database';\n const chromaApiKey = settings.CLAUDE_MEM_CHROMA_API_KEY || '';\n\n const args = [\n '--python', pythonVersion,\n ...depOverrideFlags,\n `chroma-mcp==${CHROMA_MCP_PINNED_VERSION}`,\n '--client-type', 'http',\n '--host', chromaHost,\n '--port', chromaPort\n ];\n\n args.push('--ssl', chromaSsl ? 'true' : 'false');\n\n if (chromaTenant !== 'default_tenant') {\n args.push('--tenant', chromaTenant);\n }\n\n if (chromaDatabase !== 'default_database') {\n args.push('--database', chromaDatabase);\n }\n\n if (chromaApiKey) {\n args.push('--api-key', chromaApiKey);\n }\n\n return args;\n }\n\n return [\n '--python', pythonVersion,\n ...depOverrideFlags,\n `chroma-mcp==${CHROMA_MCP_PINNED_VERSION}`,\n '--client-type', 'persistent',\n '--data-dir', DEFAULT_CHROMA_DATA_DIR.replace(/\\\\/g, '/')\n ];\n }\n\n async callTool(toolName: string, toolArguments: Record<string, unknown>): Promise<unknown> {\n await this.ensureConnected();\n\n logger.debug('CHROMA_MCP', `Calling tool: ${toolName}`, {\n arguments: JSON.stringify(toolArguments).slice(0, 200)\n });\n\n let result;\n try {\n result = await this.client!.callTool({\n name: toolName,\n arguments: toolArguments\n });\n } catch (transportError) {\n logger.warn('CHROMA_MCP', `Transport error during \"${toolName}\", reconnecting and retrying once`, {\n error: transportError instanceof Error ? transportError.message : String(transportError)\n });\n\n // Tree-kill the dying subprocess before reconnect. Previously this path\n // just nulled the handle, which on Linux leaks the uv/python/chroma-mcp\n // descendants every time a transport error happens (#2313).\n await this.disposeCurrentSubprocess();\n\n try {\n await this.ensureConnected();\n result = await this.client!.callTool({\n name: toolName,\n arguments: toolArguments\n });\n } catch (retryError) {\n this.connected = false;\n throw new Error(`chroma-mcp transport error during \"${toolName}\" (retry failed): ${retryError instanceof Error ? retryError.message : String(retryError)}`);\n }\n }\n\n if (result.isError) {\n const errorText = (result.content as Array<{ type: string; text?: string }>)\n ?.find(item => item.type === 'text')?.text || 'Unknown chroma-mcp error';\n throw new Error(`chroma-mcp tool \"${toolName}\" returned error: ${errorText}`);\n }\n\n const contentArray = result.content as Array<{ type: string; text?: string }>;\n if (!contentArray || contentArray.length === 0) {\n return null;\n }\n\n const firstTextContent = contentArray.find(item => item.type === 'text' && item.text);\n if (!firstTextContent || !firstTextContent.text) {\n return null;\n }\n\n try {\n return JSON.parse(firstTextContent.text);\n } catch (parseError: unknown) {\n if (parseError instanceof Error) {\n logger.debug('CHROMA_MCP', 'Non-JSON response from tool, returning null', {\n toolName,\n textPreview: firstTextContent.text.slice(0, 100)\n });\n }\n return null;\n }\n }\n\n async isHealthy(): Promise<boolean> {\n try {\n await this.callTool('chroma_list_collections', { limit: 1 });\n return true;\n } catch (error) {\n logger.warn('CHROMA_MCP', 'Health check failed', {\n error: error instanceof Error ? error.message : String(error)\n });\n return false;\n }\n }\n\n async probeSemanticSearch(): Promise<{\n ok: boolean;\n stage: 'connect' | 'list' | 'query' | 'done';\n error?: string;\n collections?: number;\n queryLatencyMs?: number;\n }> {\n let collections: number | undefined;\n\n try {\n const listResult: any = await this.callTool('chroma_list_collections', { limit: 100 });\n if (Array.isArray(listResult)) {\n collections = listResult.length;\n } else if (listResult && Array.isArray(listResult.collections)) {\n collections = listResult.collections.length;\n } else if (listResult && typeof listResult === 'object' && 'length' in listResult) {\n collections = (listResult as { length: number }).length;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.warn('CHROMA_MCP', 'Deep probe failed at list stage', { error: message });\n return { ok: false, stage: 'list', error: message };\n }\n\n const queryStartedAt = Date.now();\n try {\n await this.callTool('chroma_query_documents', {\n collection_name: 'cm__claude-mem',\n query_texts: ['ping'],\n n_results: 1\n });\n const queryLatencyMs = Date.now() - queryStartedAt;\n return { ok: true, stage: 'done', collections, queryLatencyMs };\n } catch (error) {\n const queryLatencyMs = Date.now() - queryStartedAt;\n const rawMessage = error instanceof Error ? error.message : String(error);\n const isMissingOrEmpty = /not exist|missing|empty|no such/i.test(rawMessage);\n const errorMessage = isMissingOrEmpty\n ? `collection cm__claude-mem missing or empty (${rawMessage})`\n : rawMessage;\n logger.warn('CHROMA_MCP', 'Deep probe failed at query stage', {\n error: rawMessage,\n queryLatencyMs\n });\n return {\n ok: false,\n stage: 'query',\n error: errorMessage,\n collections,\n queryLatencyMs\n };\n }\n }\n\n /**\n * Singleton enforcement helper (#2313): tree-kill the currently tracked\n * chroma-mcp subprocess and reset all state so the next spawn starts clean.\n *\n * Why this is the singleton invariant: every code path that intends to\n * abandon `this.transport` / `this.client` (reconnect, transport error,\n * connect-timeout, onclose, stop()) MUST funnel through here. The MCP\n * SDK's transport.close() only signals the direct child (uvx); on Linux\n * the grandchildren (uv, python, chroma-mcp) re-parent to init and\n * accumulate. Calling killProcessTree() against the captured PID before\n * we drop the reference is the only way to guarantee at most one\n * chroma-mcp subprocess tree exists per worker process.\n *\n * Idempotent and best-effort — safe to call when there is no active\n * subprocess (no-op in that case).\n */\n private async disposeCurrentSubprocess(): Promise<void> {\n const chromaProcess = (this.transport as unknown as { _process?: ChildProcess })?._process;\n const trackedPid = chromaProcess?.pid;\n\n if (trackedPid) {\n try {\n await ChromaMcpManager.killProcessTree(trackedPid);\n } catch (error) {\n logger.warn('CHROMA_MCP', 'failed to kill prior chroma-mcp tree (best-effort)', {\n pid: trackedPid,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n }\n\n if (this.transport) {\n try { await this.transport.close(); } catch { /* already dead */ }\n }\n if (this.client) {\n try { await this.client.close(); } catch { /* already dead */ }\n }\n\n if (trackedPid) {\n getSupervisor().unregisterProcess(CHROMA_SUPERVISOR_ID);\n }\n\n this.client = null;\n this.transport = null;\n this.connected = false;\n }\n\n /**\n * Gracefully stop the MCP connection and kill the chroma-mcp subprocess tree.\n *\n * The MCP SDK's client.close() sends stdin close -> SIGTERM -> SIGKILL to the\n * direct child (uvx), but the spawn chain (uvx -> uv -> python -> chroma-mcp)\n * can leave descendants orphaned because MCP SDK does not use process groups.\n *\n * Fix: kill the entire process tree rooted at the direct child PID BEFORE\n * closing the MCP client, ensuring no orphan python/chroma-mcp processes\n * accumulate across reconnects or worker restarts. Matches the tree-kill\n * pattern from shutdown.ts (Principle 5: OS-supervised teardown).\n */\n async stop(): Promise<void> {\n if (!this.client && !this.transport) {\n logger.debug('CHROMA_MCP', 'No active MCP connection to stop');\n this.connecting = null;\n return;\n }\n\n logger.info('CHROMA_MCP', 'Stopping chroma-mcp MCP connection');\n\n await this.disposeCurrentSubprocess();\n this.connecting = null;\n\n logger.info('CHROMA_MCP', 'chroma-mcp MCP connection stopped');\n }\n\n /**\n * Kill a process and all its descendants (tree-kill).\n *\n * POSIX: Sends SIGTERM to the process, then uses `pkill -P` to signal\n * children recursively. Falls back to single-PID kill if pkill is unavailable.\n *\n * Windows: Uses `taskkill /T /F /PID` for full subtree teardown (same\n * pattern as shutdown.ts).\n *\n * Best-effort — swallows ESRCH (already dead) and logs other errors.\n */\n private static async killProcessTree(pid: number): Promise<void> {\n logger.debug('CHROMA_MCP', `Killing process tree rooted at PID ${pid}`);\n\n if (process.platform === 'win32') {\n try {\n await execFileAsync('taskkill', ['/PID', String(pid), '/T', '/F'], {\n timeout: 5_000,\n windowsHide: true\n });\n } catch (error) {\n // taskkill exits non-zero when the process is already dead — that's fine.\n logger.debug('CHROMA_MCP', `taskkill tree-kill finished (may already be dead)`, {\n pid,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n return;\n }\n\n // POSIX: walk descendants recursively (bottom-up) and signal each.\n // `pkill -P <pid>` only reaches direct children, so `python` /\n // `chroma-mcp` under `uv` (grandchildren) get re-parented to init and\n // survive. We collect the full descendant set via `pgrep -P` walks before\n // signaling, so the SIGTERM phase reaches every layer\n // (CodeRabbit review on PR #2282).\n try {\n const descendantsBeforeTerm = await ChromaMcpManager.collectDescendantPids(pid);\n // Signal leaves first, then the root.\n for (const child of descendantsBeforeTerm) {\n try {\n process.kill(child, 'SIGTERM');\n } catch {\n // Already gone — fine.\n }\n }\n try {\n process.kill(pid, 'SIGTERM');\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== 'ESRCH') {\n logger.debug('CHROMA_MCP', `Failed to SIGTERM PID ${pid}`, { code });\n }\n }\n\n // Brief wait for SIGTERM to propagate, then SIGKILL stragglers.\n await new Promise(resolve => setTimeout(resolve, 500));\n\n // Re-collect descendants — some layers may have re-parented during the\n // SIGTERM grace window.\n //\n // SIGKILL targets the UNION of pre-TERM and post-wait descendant sets:\n // when the root exits between snapshots, children get re-parented to\n // init and drop out of `pgrep -P <root>`. Without the union, those\n // re-parented descendants would never receive SIGKILL even though they\n // were definitely children before SIGTERM (CodeRabbit review on PR\n // #2282). Dedupe via Set since `descendantsBeforeKill` typically\n // overlaps with `descendantsBeforeTerm`.\n const descendantsBeforeKill = await ChromaMcpManager.collectDescendantPids(pid);\n const killTargets = Array.from(new Set([...descendantsBeforeTerm, ...descendantsBeforeKill]));\n for (const child of killTargets) {\n try {\n process.kill(child, 'SIGKILL');\n } catch {\n // Already dead — fine.\n }\n }\n try {\n process.kill(pid, 'SIGKILL');\n } catch {\n // Already dead — fine.\n }\n } catch (error) {\n logger.debug('CHROMA_MCP', `Process tree kill completed (best-effort)`, {\n pid,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n }\n\n /**\n * Recursively collect all descendant PIDs of `rootPid` using `pgrep -P`.\n * Returned bottom-up (leaves first) so callers can signal leaves before\n * their ancestors. Best-effort: missing pgrep / non-zero exits return [].\n */\n private static async collectDescendantPids(rootPid: number): Promise<number[]> {\n const seen = new Set<number>();\n const collected: number[] = [];\n\n async function walk(pid: number): Promise<void> {\n let stdout = '';\n try {\n const result = await execFileAsync('pgrep', ['-P', String(pid)], { timeout: 2_000 });\n stdout = result.stdout;\n } catch {\n // pgrep exits 1 when no children match — that's fine, just return.\n return;\n }\n const children = stdout\n .split('\\n')\n .map(line => line.trim())\n .filter(line => line.length > 0)\n .map(line => Number.parseInt(line, 10))\n .filter(n => Number.isFinite(n) && n > 0 && !seen.has(n));\n\n for (const child of children) {\n seen.add(child);\n await walk(child);\n // Bottom-up: push after recursion so leaves come first.\n collected.push(child);\n }\n }\n\n await walk(rootPid);\n return collected;\n }\n\n /**\n * Reset the singleton instance (for testing).\n * Awaits stop() to prevent dual subprocesses.\n */\n static async reset(): Promise<void> {\n if (ChromaMcpManager.instance) {\n await ChromaMcpManager.instance.stop();\n }\n ChromaMcpManager.instance = null;\n }\n\n private getCombinedCertPath(): string | undefined {\n const combinedCertPath = paths.combinedCerts();\n\n if (fs.existsSync(combinedCertPath)) {\n const stats = fs.statSync(combinedCertPath);\n const ageMs = Date.now() - stats.mtimeMs;\n if (ageMs < 24 * 60 * 60 * 1000) {\n return combinedCertPath;\n }\n }\n\n if (process.platform !== 'darwin') {\n return undefined;\n }\n\n try {\n let certifiPath: string | undefined;\n try {\n certifiPath = execSync(\n 'uvx --with certifi python -c \"import certifi; print(certifi.where())\"',\n { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000 }\n ).trim();\n } catch (error) {\n logger.debug('CHROMA_MCP', 'Failed to resolve certifi path via uvx', {\n error: error instanceof Error ? error.message : String(error)\n });\n return undefined;\n }\n\n if (!certifiPath || !fs.existsSync(certifiPath)) {\n return undefined;\n }\n\n let zscalerCert = '';\n try {\n zscalerCert = execSync(\n 'security find-certificate -a -c \"Zscaler\" -p /Library/Keychains/System.keychain',\n { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 5000 }\n );\n } catch (error) {\n logger.debug('CHROMA_MCP', 'No Zscaler certificate found in system keychain', {\n error: error instanceof Error ? error.message : String(error)\n });\n return undefined;\n }\n\n if (!zscalerCert ||\n !zscalerCert.includes('-----BEGIN CERTIFICATE-----') ||\n !zscalerCert.includes('-----END CERTIFICATE-----')) {\n return undefined;\n }\n\n const certifiContent = fs.readFileSync(certifiPath, 'utf8');\n const tempPath = combinedCertPath + '.tmp';\n fs.writeFileSync(tempPath, certifiContent + '\\n' + zscalerCert);\n fs.renameSync(tempPath, combinedCertPath);\n\n logger.info('CHROMA_MCP', 'Created combined SSL certificate bundle for Zscaler', {\n path: combinedCertPath\n });\n\n return combinedCertPath;\n } catch (error) {\n logger.debug('CHROMA_MCP', 'Could not create combined cert bundle', {}, error as Error);\n return undefined;\n }\n }\n\n /**\n * uv installs `uvx` to a per-user bin dir that is often NOT on the PATH the\n * worker inherited (the worker is spawned by the host with a minimal env that\n * predates the user adding uv to PATH). Without it, `uvx`/`cmd /c uvx` dies\n * with \"not recognized\" in ~25ms and semantic search silently falls back to\n * keyword (#2790). Prepend uv's known bin dirs (and an explicit override) so\n * the chroma child can always resolve uvx. Additive and cross-platform — only\n * dirs that exist are added.\n */\n private static uvBinDirs(): string[] {\n const home = os.homedir();\n const dirs = [\n process.env.CLAUDE_MEM_CHROMA_UVX_PATH, // explicit override (dir or uvx path)\n path.join(home, '.local', 'bin'), // uv default (Windows + Unix)\n path.join(home, '.cargo', 'bin'), // cargo-installed uv\n ].filter((d): d is string => Boolean(d));\n // If the override points at the uvx binary itself, use its directory.\n return dirs.map(d => {\n try {\n return fs.existsSync(d) && fs.statSync(d).isFile() ? path.dirname(d) : d;\n } catch {\n return d;\n }\n });\n }\n\n /**\n * Resolve the command used to launch uvx.\n *\n * On Windows we MUST spawn uvx.exe DIRECTLY rather than via `cmd.exe /c uvx`:\n * cmd.exe parses the `>`/`<` in the dep-override specs (onnxruntime>=1.20,\n * protobuf<7) as shell redirection before uvx sees them, and Node's\n * child_process arg-quoting for cmd.exe re-mangles even pre-quoted args, so\n * cmd.exe dies with \"The directory name is invalid\" in ~10ms. The MCP\n * transport then reports \"Connection closed\", the manager backs off, and\n * semantic search silently degrades to keyword-only (#2696 follow-up).\n *\n * Node's shell-less spawn won't resolve a bare `uvx` via PATHEXT on Windows,\n * so we resolve the absolute path to uvx.exe from the same uv bin dirs that\n * ensureUvOnPath() adds to the child PATH (honouring CLAUDE_MEM_CHROMA_UVX_PATH\n * when it points straight at a binary), falling back to bare 'uvx.exe'.\n */\n static resolveUvxCommand(platform: NodeJS.Platform = process.platform): string {\n if (platform !== 'win32') {\n return 'uvx';\n }\n const override = process.env.CLAUDE_MEM_CHROMA_UVX_PATH;\n if (override) {\n try {\n if (fs.existsSync(override) && fs.statSync(override).isFile()) {\n return override;\n }\n } catch {\n // fall through to scanning the known uv bin dirs\n }\n }\n for (const dir of ChromaMcpManager.uvBinDirs()) {\n const candidate = path.join(dir, 'uvx.exe');\n try {\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n } catch {\n // ignore and try the next candidate\n }\n }\n return 'uvx.exe';\n }\n\n private static ensureUvOnPath(env: Record<string, string>): void {\n const sep = process.platform === 'win32' ? ';' : ':';\n const pathKey = Object.keys(env).find(k => k.toLowerCase() === 'path') ?? 'PATH';\n const current = env[pathKey] ? env[pathKey].split(sep).filter(Boolean) : [];\n const have = new Set(current.map(p => (process.platform === 'win32' ? p.toLowerCase() : p)));\n const additions = ChromaMcpManager.uvBinDirs().filter(dir => {\n try {\n if (!fs.existsSync(dir)) return false;\n } catch {\n return false;\n }\n const key = process.platform === 'win32' ? dir.toLowerCase() : dir;\n return !have.has(key);\n });\n if (additions.length > 0) {\n env[pathKey] = [...additions, ...current].join(sep);\n logger.debug('CHROMA_MCP', 'Prepended uv bin dir(s) to chroma child PATH', { added: additions });\n }\n }\n\n private getSpawnEnv(): Record<string, string> {\n const baseEnv: Record<string, string> = {};\n for (const [key, value] of Object.entries(sanitizeEnv(process.env))) {\n if (value !== undefined) {\n baseEnv[key] = value;\n }\n }\n\n // Ensure uvx is resolvable even if the worker's inherited PATH omits uv's\n // bin dir (#2790).\n ChromaMcpManager.ensureUvOnPath(baseEnv);\n\n // Disable Chroma's anonymous telemetry — it issues background HTTP from\n // the embedding subprocess on every collection touch.\n if (!baseEnv.ANONYMIZED_TELEMETRY) baseEnv.ANONYMIZED_TELEMETRY = 'false';\n\n const combinedCertPath = this.getCombinedCertPath();\n if (!combinedCertPath) {\n return baseEnv;\n }\n\n logger.info('CHROMA_MCP', 'Using combined SSL certificates for enterprise compatibility', {\n certPath: combinedCertPath\n });\n\n return {\n ...baseEnv,\n SSL_CERT_FILE: combinedCertPath,\n REQUESTS_CA_BUNDLE: combinedCertPath,\n CURL_CA_BUNDLE: combinedCertPath,\n NODE_EXTRA_CA_CERTS: combinedCertPath\n };\n }\n\n private registerManagedProcess(): void {\n const chromaProcess = (this.transport as unknown as { _process?: ChildProcess })._process;\n if (!chromaProcess?.pid) {\n return;\n }\n\n // Register with pgid so the supervisor's shutdown cascade can use\n // process-group signaling (kill(-pgid, signal)) to tear down the\n // entire spawn chain (uvx -> uv -> python -> chroma-mcp) in one\n // syscall, matching the SDK subprocess pattern in process-registry.ts.\n //\n // Note: MCP SDK's StdioClientTransport does NOT use detached:true,\n // so the child shares our process group — setting pgid here enables\n // tree-kill via signalProcess() in shutdown.ts which falls back to\n // taskkill /T on Windows when pgid is present but group signal fails.\n // On POSIX the pgid recorded here is used by killProcessTree() in\n // stop() for explicit tree teardown rather than negative-PID signaling.\n getSupervisor().registerProcess(CHROMA_SUPERVISOR_ID, {\n pid: chromaProcess.pid,\n type: 'chroma',\n startedAt: new Date().toISOString(),\n // Store pid as pgid — shutdown.ts will attempt kill(-pgid) on POSIX.\n // If the child isn't actually its own group leader, the ESRCH is caught\n // and shutdown falls back to single-PID kill (see signalProcess()).\n pgid: chromaProcess.pid\n }, chromaProcess);\n\n chromaProcess.once('exit', () => {\n getSupervisor().unregisterProcess(CHROMA_SUPERVISOR_ID);\n });\n }\n}\n","\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { paths } from '../shared/paths.js';\nimport { emitDiagnostic } from '../shared/hook-io.js';\n\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n SILENT = 4\n}\n\nexport type Component =\n | 'AGENTS_MD'\n | 'BRANCH'\n | 'CHROMA'\n | 'CHROMA_MCP'\n | 'CHROMA_SYNC'\n | 'CLAUDE_MD'\n | 'CONFIG'\n | 'CONSOLE'\n | 'CURSOR'\n | 'DB'\n | 'DEDUP'\n | 'ENV'\n | 'FOLDER_INDEX'\n | 'GIT'\n | 'HOOK'\n | 'HTTP'\n | 'IMPORT'\n | 'INGEST'\n | 'OAUTH'\n | 'OPENCLAW'\n | 'OPENCODE'\n | 'PARSER'\n | 'PROCESS'\n | 'PROJECT_NAME'\n | 'QUEUE'\n | 'SDK'\n | 'SDK_SPAWN'\n | 'SEARCH'\n | 'SECURITY'\n | 'SESSION'\n | 'SETTINGS'\n | 'SHUTDOWN'\n | 'SYSTEM'\n | 'TELEGRAM'\n | 'TRANSCRIPT'\n | 'WINDSURF'\n | 'WORKER';\n\ninterface LogContext {\n sessionId?: string | number;\n memorySessionId?: string;\n correlationId?: string | number;\n [key: string]: any;\n}\n\n/**\n * Optional error sink. The logger must NEVER import the telemetry client (that\n * would create an import cycle: telemetry → logger via instrument.ts → ...).\n * Instead worker/telemetry init injects a sink via logger.setErrorSink(); when\n * present, logger.error()/logger.failure() route their Error payload through it\n * (consent + rate-limit + kill-switch all enforced INSIDE the sink, i.e.\n * captureException). The sink is optional and swallow-all so logging keeps\n * working with telemetry disabled or uninstalled.\n */\nexport type ErrorSink = (err: unknown, ctx?: Record<string, unknown>) => void;\nlet errorSink: ErrorSink | null = null;\n\n\nclass Logger {\n private level: LogLevel | null = null;\n private useColor: boolean;\n private logFilePath: string | null = null;\n private logFileInitialized: boolean = false;\n\n constructor() {\n this.useColor = process.stdout.isTTY ?? false;\n // Don't initialize log file in constructor - do it lazily to avoid circular dependency\n }\n\n private ensureLogFileInitialized(): void {\n if (this.logFileInitialized) return;\n this.logFileInitialized = true;\n\n try {\n const logsDir = paths.logsDir();\n\n if (!existsSync(logsDir)) {\n mkdirSync(logsDir, { recursive: true });\n }\n\n const date = new Date().toISOString().split('T')[0];\n this.logFilePath = join(logsDir, `claude-mem-${date}.log`);\n } catch (error: unknown) {\n console.error('[LOGGER] Failed to initialize log file:', error instanceof Error ? error.message : String(error));\n this.logFilePath = null;\n }\n }\n\n private getLevel(): LogLevel {\n if (this.level === null) {\n try {\n const settingsPath = paths.settings();\n if (existsSync(settingsPath)) {\n const settingsData = readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(settingsData);\n const envLevel = (settings.CLAUDE_MEM_LOG_LEVEL || 'INFO').toUpperCase();\n this.level = LogLevel[envLevel as keyof typeof LogLevel] ?? LogLevel.INFO;\n } else {\n this.level = LogLevel.INFO;\n }\n } catch (error: unknown) {\n console.error('[LOGGER] Failed to load log level from settings:', error instanceof Error ? error.message : String(error));\n this.level = LogLevel.INFO;\n }\n }\n return this.level;\n }\n\n private formatData(data: any): string {\n if (data === null || data === undefined) return '';\n if (typeof data === 'string') return data;\n if (typeof data === 'number') return data.toString();\n if (typeof data === 'boolean') return data.toString();\n\n if (typeof data === 'object') {\n if (data instanceof Error) {\n return this.getLevel() === LogLevel.DEBUG\n ? `${data.message}\\n${data.stack}`\n : data.message;\n }\n\n if (Array.isArray(data)) {\n return `[${data.length} items]`;\n }\n\n const keys = Object.keys(data);\n if (keys.length === 0) return '{}';\n if (keys.length <= 3) {\n return JSON.stringify(data);\n }\n return `{${keys.length} keys: ${keys.slice(0, 3).join(', ')}...}`;\n }\n\n return String(data);\n }\n\n formatTool(toolName: string, toolInput?: any): string {\n if (!toolInput) return toolName;\n\n let input = toolInput;\n if (typeof toolInput === 'string') {\n try {\n input = JSON.parse(toolInput);\n } catch (_parseError: unknown) {\n input = toolInput;\n }\n }\n\n if (toolName === 'Bash' && input.command) {\n return `${toolName}(${input.command})`;\n }\n\n if (input.file_path) {\n return `${toolName}(${input.file_path})`;\n }\n\n if (input.notebook_path) {\n return `${toolName}(${input.notebook_path})`;\n }\n\n if (toolName === 'Glob' && input.pattern) {\n return `${toolName}(${input.pattern})`;\n }\n\n if (toolName === 'Grep' && input.pattern) {\n return `${toolName}(${input.pattern})`;\n }\n\n if (input.url) {\n return `${toolName}(${input.url})`;\n }\n\n if (input.query) {\n return `${toolName}(${input.query})`;\n }\n\n if (toolName === 'Task') {\n if (input.subagent_type) {\n return `${toolName}(${input.subagent_type})`;\n }\n if (input.description) {\n return `${toolName}(${input.description})`;\n }\n }\n\n if (toolName === 'Skill' && input.skill) {\n return `${toolName}(${input.skill})`;\n }\n\n if (toolName === 'LSP' && input.operation) {\n return `${toolName}(${input.operation})`;\n }\n\n return toolName;\n }\n\n private formatTimestamp(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n const hours = String(date.getHours()).padStart(2, '0');\n const minutes = String(date.getMinutes()).padStart(2, '0');\n const seconds = String(date.getSeconds()).padStart(2, '0');\n const ms = String(date.getMilliseconds()).padStart(3, '0');\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;\n }\n\n private log(\n level: LogLevel,\n component: Component,\n message: string,\n context?: LogContext,\n data?: any\n ): void {\n if (level < this.getLevel()) return;\n\n this.ensureLogFileInitialized();\n\n const timestamp = this.formatTimestamp(new Date());\n const levelStr = LogLevel[level].padEnd(5);\n const componentStr = component.padEnd(6);\n\n let correlationStr = '';\n if (context?.correlationId) {\n correlationStr = `[${context.correlationId}] `;\n } else if (context?.sessionId) {\n correlationStr = `[session-${context.sessionId}] `;\n }\n\n let dataStr = '';\n if (data !== undefined && data !== null) {\n if (data instanceof Error) {\n dataStr = this.getLevel() === LogLevel.DEBUG\n ? `\\n${data.message}\\n${data.stack}`\n : ` ${data.message}`;\n } else if (this.getLevel() === LogLevel.DEBUG && typeof data === 'object') {\n try {\n dataStr = '\\n' + JSON.stringify(data, null, 2);\n } catch {\n dataStr = ' ' + this.formatData(data);\n }\n } else {\n dataStr = ' ' + this.formatData(data);\n }\n }\n\n let contextStr = '';\n if (context) {\n const { sessionId, memorySessionId, correlationId, ...rest } = context;\n if (Object.keys(rest).length > 0) {\n const pairs = Object.entries(rest).map(([k, v]) => `${k}=${v}`);\n contextStr = ` {${pairs.join(', ')}}`;\n }\n }\n\n const logLine = `[${timestamp}] [${levelStr}] [${componentStr}] ${correlationStr}${message}${contextStr}${dataStr}`;\n\n if (this.logFilePath) {\n try {\n appendFileSync(this.logFilePath, logLine + '\\n', 'utf8');\n } catch (error: unknown) {\n // DIAGNOSTIC: route through hook-io so the message bypasses the Phase 2\n // hook stderr buffer (#2292). Outside the hook context emitDiagnostic\n // writes straight to real stderr, so non-hook callers are unaffected.\n emitDiagnostic(`[LOGGER] Failed to write to log file: ${error instanceof Error ? error.message : String(error)}\\n`);\n }\n } else {\n // DIAGNOSTIC: see note above.\n emitDiagnostic(logLine + '\\n');\n }\n }\n\n debug(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.DEBUG, component, message, context, data);\n }\n\n info(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.INFO, component, message, context, data);\n }\n\n warn(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.WARN, component, message, context, data);\n }\n\n /**\n * Installs (or clears, with null) the optional error sink. Called once by\n * worker/telemetry init to bridge logged errors into captureException without\n * the logger importing telemetry (no import cycle). Never throws.\n */\n setErrorSink(sink: ErrorSink | null): void {\n errorSink = sink;\n }\n\n error(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.ERROR, component, message, context, data);\n this.routeErrorToSink(message, context, data);\n }\n\n /**\n * Routes a logged Error through the optional error sink (captureException).\n * Only fires when `data` is an actual Error so we never ship arbitrary log\n * payloads as exceptions. Swallow-all: the sink failing (or being absent)\n * must never break logging. `failure()` delegates to `error()`, so it is\n * covered too — but it passes the same `data` object, so we de-dupe by only\n * routing from the single `error()` entry point.\n */\n private routeErrorToSink(message: string, context?: LogContext, data?: any): void {\n try {\n if (!errorSink || !(data instanceof Error)) return;\n // Pass the message as context so the sink can fingerprint on it too; the\n // sink (captureException) scrubs everything through error-scrub /\n // scrubProperties, so an unsafe message here cannot leak — but `message`\n // is not whitelisted, so it is dropped by scrubProperties anyway. We pass\n // only the Error itself; context is intentionally minimal.\n errorSink(data);\n } catch {\n // Telemetry/error-sink must never break logging.\n }\n }\n\n dataIn(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `→ ${message}`, context, data);\n }\n\n dataOut(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `← ${message}`, context, data);\n }\n\n success(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `✓ ${message}`, context, data);\n }\n\n failure(component: Component, message: string, context?: LogContext, data?: any): void {\n this.error(component, `✗ ${message}`, context, data);\n }\n\n happyPathError<T = string>(\n component: Component,\n message: string,\n context?: LogContext,\n data?: any,\n fallback: T = '' as T\n ): T {\n const stack = new Error().stack || '';\n const stackLines = stack.split('\\n');\n const callerLine = stackLines[2] || '';\n const callerMatch = callerLine.match(/at\\s+(?:.*\\s+)?\\(?([^:]+):(\\d+):(\\d+)\\)?/);\n const location = callerMatch\n ? `${callerMatch[1].split('/').pop()}:${callerMatch[2]}`\n : 'unknown';\n\n const enhancedContext = {\n ...context,\n location\n };\n\n this.warn(component, `[HAPPY-PATH] ${message}`, enhancedContext, data);\n\n return fallback;\n }\n}\n\nexport const logger = new Logger();\n","import { join, dirname, basename, sep } from 'path';\nimport { homedir } from 'os';\nimport { existsSync, mkdirSync, readFileSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { fileURLToPath } from 'url';\nimport { SettingsDefaultsManager } from './SettingsDefaultsManager.js';\nimport { logger } from '../utils/logger.js';\n\nfunction getDirname(): string {\n if (typeof __dirname !== 'undefined') {\n return __dirname;\n }\n return dirname(fileURLToPath(import.meta.url));\n}\n\nconst _dirname = getDirname();\n\nexport function resolveDataDir(): string {\n if (process.env.CLAUDE_MEM_DATA_DIR) {\n return process.env.CLAUDE_MEM_DATA_DIR;\n }\n\n const defaultDataDir = join(homedir(), '.claude-mem');\n const settingsPath = join(defaultDataDir, 'settings.json');\n try {\n if (existsSync(settingsPath)) {\n const raw = JSON.parse(readFileSync(settingsPath, 'utf-8'));\n const settings = raw.env ?? raw; \n if (settings.CLAUDE_MEM_DATA_DIR) {\n return settings.CLAUDE_MEM_DATA_DIR;\n }\n }\n } catch {\n // settings file missing or corrupt — fall through to default\n }\n\n return defaultDataDir;\n}\n\nexport const DATA_DIR = resolveDataDir();\nexport const CLAUDE_CONFIG_DIR = process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude');\n\nexport const MARKETPLACE_ROOT = join(CLAUDE_CONFIG_DIR, 'plugins', 'marketplaces', 'thedotmack');\n\nexport const ARCHIVES_DIR = join(DATA_DIR, 'archives');\nexport const LOGS_DIR = join(DATA_DIR, 'logs');\nexport const TRASH_DIR = join(DATA_DIR, 'trash');\nexport const BACKUPS_DIR = join(DATA_DIR, 'backups');\nexport const MODES_DIR = join(DATA_DIR, 'modes');\nexport const USER_SETTINGS_PATH = join(DATA_DIR, 'settings.json');\nexport const DB_PATH = join(DATA_DIR, 'claude-mem.db');\nexport const VECTOR_DB_DIR = join(DATA_DIR, 'vector-db');\n\nexport const OBSERVER_SESSIONS_DIR = join(DATA_DIR, 'observer-sessions');\n\nexport const OBSERVER_SESSIONS_PROJECT = basename(OBSERVER_SESSIONS_DIR);\n\nexport const CLAUDE_SETTINGS_PATH = join(CLAUDE_CONFIG_DIR, 'settings.json');\nexport const CLAUDE_COMMANDS_DIR = join(CLAUDE_CONFIG_DIR, 'commands');\nexport const CLAUDE_MD_PATH = join(CLAUDE_CONFIG_DIR, 'CLAUDE.md');\n\nexport function getProjectArchiveDir(projectName: string): string {\n return join(ARCHIVES_DIR, projectName);\n}\n\nexport function ensureDir(dirPath: string): void {\n mkdirSync(dirPath, { recursive: true });\n}\n\nexport function ensureAllDataDirs(): void {\n ensureDir(DATA_DIR);\n ensureDir(ARCHIVES_DIR);\n ensureDir(LOGS_DIR);\n ensureDir(TRASH_DIR);\n ensureDir(BACKUPS_DIR);\n ensureDir(MODES_DIR);\n}\n\nexport function ensureModesDir(): void {\n ensureDir(MODES_DIR);\n}\n\nexport function ensureAllClaudeDirs(): void {\n ensureDir(CLAUDE_CONFIG_DIR);\n ensureDir(CLAUDE_COMMANDS_DIR);\n}\n\nexport function getCurrentProjectName(): string {\n try {\n const gitRoot = execSync('git rev-parse --show-toplevel', {\n cwd: process.cwd(),\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'ignore'],\n windowsHide: true\n }).trim();\n return basename(dirname(gitRoot)) + '/' + basename(gitRoot);\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'Git root detection failed, using cwd basename', {\n cwd: process.cwd()\n }, error instanceof Error ? error : new Error(String(error)));\n const cwd = process.cwd();\n return basename(dirname(cwd)) + '/' + basename(cwd);\n }\n}\n\nexport function getPackageRoot(): string {\n return join(_dirname, '..');\n}\n\nexport function getPackageCommandsDir(): string {\n const packageRoot = getPackageRoot();\n return join(packageRoot, 'commands');\n}\n\nexport const paths = {\n dataDir: () => DATA_DIR,\n workerPid: () => join(DATA_DIR, 'worker.pid'),\n // Phase 1b: identifier renamed to `server*`; the on-disk file basenames\n // remain `.server-beta.*` so existing installations keep finding their\n // pid/port/runtime state. Plan §1d will migrate the basenames.\n serverPid: () => join(DATA_DIR, '.server-beta.pid'),\n serverPort: () => join(DATA_DIR, '.server-beta.port'),\n serverRuntime: () => join(DATA_DIR, '.server-beta.runtime.json'),\n settings: () => join(DATA_DIR, 'settings.json'),\n database: () => join(DATA_DIR, 'claude-mem.db'),\n chroma: () => join(DATA_DIR, 'chroma'),\n combinedCerts: () => join(DATA_DIR, 'combined_certs.pem'),\n transcriptsConfig: () => join(DATA_DIR, 'transcript-watch.json'),\n transcriptsState: () => join(DATA_DIR, 'transcript-watch-state.json'),\n corpora: () => join(DATA_DIR, 'corpora'),\n supervisorRegistry: () => join(DATA_DIR, 'supervisor.json'),\n envFile: () => join(DATA_DIR, '.env'),\n logsDir: () => LOGS_DIR,\n archives: () => ARCHIVES_DIR,\n trash: () => TRASH_DIR,\n backups: () => BACKUPS_DIR,\n modes: () => MODES_DIR,\n vectorDb: () => VECTOR_DB_DIR,\n observerSessions: () => OBSERVER_SESSIONS_DIR,\n} as const;\n","/**\n * Hook IO Discipline (issue #2292)\n *\n * This module is the ONLY place in the hook execution path that calls\n * console.log / process.stderr.write / process.exit. Every emit point declares\n * an intent and routes through here so stdout (MODEL_CONTEXT), stderr\n * (DIAGNOSTIC / USER_HINT), and exit codes (EXIT_SIGNAL / BLOCKING_FEEDBACK)\n * never get conflated.\n *\n * Intent vocabulary:\n * - DIAGNOSTIC operator-visible logs, never reaches the model. stderr.\n * - MODEL_CONTEXT content the assistant consumes. stdout JSON only.\n * - USER_HINT short advisory shown to the human, via HookResult.systemMessage.\n * - BLOCKING_FEEDBACK error message the model must see (stderr + exit 2).\n * - EXIT_SIGNAL pure status, no payload (exit 0).\n *\n * Lives in src/shared/ (not src/cli/) so that src/shared/worker-utils.ts and\n * src/utils/logger.ts can route their stderr through emitDiagnostic without a\n * shared->cli runtime dependency. Only the HookResult / PlatformAdapter TYPES\n * are imported from src/cli, and `import type` is erased at runtime.\n */\nimport type { PlatformAdapter, HookResult } from '../cli/types.js';\n\nexport interface HookStderrBuffer {\n /** Write buffered bytes to real stderr, then clear the buffer. */\n flush(): void;\n /** Discard buffered bytes without writing them. */\n drop(): void;\n /** Un-replace process.stderr.write (idempotent). */\n restore(): void;\n}\n\ntype StderrWriter = (chunk: string | Uint8Array) => boolean;\n\n/**\n * The bypass channel: emitDiagnostic, emitBlockingError, and the buffer's\n * flush() all write through this so they skip the buffered window.\n *\n * - When NO buffer is installed it resolves to the live process.stderr.write\n * (so non-hook callers — worker daemon, CLI — write straight to stderr).\n * - installHookStderrBuffer() pins it to the writer that was active at install\n * time (the real fd writer), so flushing the buffer never re-enters the\n * buffered writer.\n */\nlet pinnedBypassWrite: StderrWriter | null = null;\n\nfunction bypassWrite(chunk: string | Uint8Array): boolean {\n const writer = pinnedBypassWrite\n ?? (process.stderr.write.bind(process.stderr) as StderrWriter);\n return writer(chunk);\n}\n\nlet bufferedChunks: string[] | null = null;\nlet bufferInstalled = false;\n\n/**\n * Replace process.stderr.write with a buffered writer. Direct\n * process.stderr.write calls (including unsolicited third-party library noise)\n * are captured into a buffer; emitDiagnostic / emitBlockingError write through\n * the bypass channel (realStderrWrite). The buffer is flushed when claude-mem\n * chooses to surface, and dropped on graceful success.\n */\nexport function installHookStderrBuffer(): HookStderrBuffer {\n // Pin the currently-active stderr writer as the bypass channel BEFORE we\n // replace process.stderr.write, so flush()/emitDiagnostic write to the real\n // fd and never re-enter the buffered writer.\n const realStderrWrite = process.stderr.write.bind(process.stderr) as StderrWriter;\n pinnedBypassWrite = realStderrWrite;\n bufferedChunks = [];\n bufferInstalled = true;\n\n process.stderr.write = ((chunk: string | Uint8Array): boolean => {\n if (bufferedChunks) {\n bufferedChunks.push(typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf-8'));\n }\n return true;\n }) as typeof process.stderr.write;\n\n return {\n flush(): void {\n if (bufferedChunks && bufferedChunks.length > 0) {\n realStderrWrite(bufferedChunks.join(''));\n }\n bufferedChunks = [];\n },\n drop(): void {\n bufferedChunks = [];\n },\n restore(): void {\n if (!bufferInstalled) return;\n process.stderr.write = realStderrWrite as typeof process.stderr.write;\n bufferInstalled = false;\n bufferedChunks = null;\n pinnedBypassWrite = null;\n },\n };\n}\n\n/**\n * Operator-visible diagnostic. Always reaches real stderr (bypasses the\n * buffer). Use for logger fallback, fail-loud counter, and any \"we want this\n * in the operator's terminal\" message. Takes a raw string; keep logger.* as\n * the structured-logging path.\n */\nexport function emitDiagnostic(line: string): void {\n bypassWrite(line);\n}\n\n/**\n * Emit the model-bound JSON payload to stdout. Calls adapter.formatOutput and\n * JSON.stringify exactly once. Throws if called twice in the same emitter\n * lifetime (guards against double-emit corrupting the stdout JSON stream).\n *\n * Uses console.log (not process.stdout.write) on purpose: the trailing newline\n * is what Claude Code's / Codex's hook parser expects.\n */\nexport function emitModelContext(adapter: PlatformAdapter, result: HookResult): void {\n if (moduleHasEmitted) {\n throw new Error('emitModelContext called twice');\n }\n moduleHasEmitted = true;\n const output = adapter.formatOutput(result);\n console.log(JSON.stringify(output));\n}\n\nlet moduleHasEmitted = false;\n\n/**\n * USER_HINT routed via the HookResult.systemMessage path. Does NOT write to a\n * stream — returns a HookResult the caller merges before emitModelContext.\n * systemMessage is platform-specific (claude-code surfaces it; codex ignores\n * it) so it must flow through the adapter, not raw stderr. If a systemMessage\n * already exists, the hint is appended with a blank line between.\n */\nexport function withUserHint(result: HookResult, hint: string): HookResult {\n const merged = result.systemMessage ? `${result.systemMessage}\\n\\n${hint}` : hint;\n return { ...result, systemMessage: merged };\n}\n\nexport interface ExitOptions {\n skipExit?: boolean;\n}\n\n/**\n * BLOCKING_FEEDBACK: flush buffered stderr (so preceding diagnostics reach the\n * operator/model), write `msg` to real stderr, then exit 2 so the model\n * receives it per Claude Code's hook contract. `skipExit` is the test seam\n * that mirrors HookCommandOptions.skipExit.\n */\nexport function emitBlockingError(msg: string, options: ExitOptions = {}): void {\n if (bufferedChunks && bufferedChunks.length > 0) {\n bypassWrite(bufferedChunks.join(''));\n bufferedChunks = [];\n }\n bypassWrite(msg.endsWith('\\n') ? msg : `${msg}\\n`);\n if (!options.skipExit) {\n process.exit(2);\n }\n}\n\n/**\n * EXIT_SIGNAL: drop any buffered stderr (preserving the quiet-on-success /\n * Windows Terminal tab-management behavior) and exit 0. Caller is expected to\n * have already emitted any required stdout JSON envelope.\n */\nexport function exitGraceful(options: ExitOptions = {}): void {\n if (bufferedChunks) {\n bufferedChunks = [];\n }\n if (!options.skipExit) {\n process.exit(0);\n }\n}\n\n/**\n * Reset the per-invocation emit flag. hookCommand calls this at the start of\n * each invocation so the emitModelContext double-emit guard is per-hook, not\n * per-process (matters for the in-process test harness and skipExit tests).\n */\nexport function resetHookIoState(): void {\n moduleHasEmitted = false;\n}\n","\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { homedir } from 'os';\nimport { HOOK_TIMEOUTS, getTimeout } from './hook-constants.js';\n\nexport interface SettingsDefaults {\n CLAUDE_MEM_MODEL: string;\n CLAUDE_MEM_CONTEXT_OBSERVATIONS: string;\n CLAUDE_MEM_WORKER_PORT: string;\n CLAUDE_MEM_WORKER_HOST: string;\n CLAUDE_MEM_API_TIMEOUT_MS: string;\n CLAUDE_MEM_SKIP_TOOLS: string;\n CLAUDE_MEM_PROVIDER: string; \n CLAUDE_MEM_CLAUDE_AUTH_METHOD: string; \n CLAUDE_MEM_GEMINI_API_KEY: string;\n CLAUDE_MEM_GEMINI_MODEL: string; \n CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED: string; \n CLAUDE_MEM_GEMINI_MAX_CONTEXT_MESSAGES: string; \n CLAUDE_MEM_GEMINI_MAX_TOKENS: string; \n CLAUDE_MEM_OPENROUTER_API_KEY: string;\n CLAUDE_MEM_OPENROUTER_MODEL: string;\n CLAUDE_MEM_OPENROUTER_BASE_URL: string;\n CLAUDE_MEM_OPENROUTER_SITE_URL: string;\n CLAUDE_MEM_OPENROUTER_APP_NAME: string;\n CLAUDE_MEM_OPENROUTER_MAX_CONTEXT_MESSAGES: string;\n CLAUDE_MEM_OPENROUTER_MAX_TOKENS: string;\n CLAUDE_MEM_DATA_DIR: string;\n CLAUDE_MEM_LOG_LEVEL: string;\n CLAUDE_MEM_PYTHON_VERSION: string;\n CLAUDE_CODE_PATH: string;\n CLAUDE_MEM_MODE: string;\n CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: string;\n CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: string;\n CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: string;\n CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: string;\n CLAUDE_MEM_CONTEXT_FULL_COUNT: string;\n CLAUDE_MEM_CONTEXT_FULL_FIELD: string;\n CLAUDE_MEM_CONTEXT_SESSION_COUNT: string;\n CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: string;\n CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: string;\n CLAUDE_MEM_CONTEXT_SHOW_TERMINAL_OUTPUT: string;\n CLAUDE_MEM_WELCOME_HINT_ENABLED: string;\n CLAUDE_MEM_FOLDER_CLAUDEMD_ENABLED: string;\n CLAUDE_MEM_FOLDER_USE_LOCAL_MD: string; \n CLAUDE_MEM_TRANSCRIPTS_ENABLED: string; \n CLAUDE_MEM_TRANSCRIPTS_CONFIG_PATH: string; \n CLAUDE_MEM_CODEX_TRANSCRIPT_INGESTION: string;\n CLAUDE_MEM_MAX_CONCURRENT_AGENTS: string; \n CLAUDE_MEM_HOOK_FAIL_LOUD_THRESHOLD: string; \n CLAUDE_MEM_EXCLUDED_PROJECTS: string; \n CLAUDE_MEM_FOLDER_MD_EXCLUDE: string;\n CLAUDE_MEM_FOLDER_MD_SKELETON_DENYLIST: string;\n CLAUDE_MEM_SEMANTIC_INJECT: string; \n CLAUDE_MEM_SEMANTIC_INJECT_LIMIT: string; \n CLAUDE_MEM_TIER_ROUTING_ENABLED: string;\n CLAUDE_MEM_TIER_SIMPLE_MODEL: string;\n CLAUDE_MEM_TIER_SUMMARY_MODEL: string;\n CLAUDE_MEM_TIER_FAST_MODEL: string; // #2289 — resolved by $TIER:fast in CLAUDE_MEM_MODEL\n CLAUDE_MEM_TIER_SMART_MODEL: string; // #2289 — resolved by $TIER:smart in CLAUDE_MEM_MODEL\n CLAUDE_MEM_CHROMA_ENABLED: string; \n CLAUDE_MEM_CHROMA_MODE: string; \n CLAUDE_MEM_CHROMA_HOST: string;\n CLAUDE_MEM_CHROMA_PORT: string;\n CLAUDE_MEM_CHROMA_SSL: string;\n CLAUDE_MEM_CHROMA_API_KEY: string;\n CLAUDE_MEM_CHROMA_TENANT: string;\n CLAUDE_MEM_CHROMA_DATABASE: string;\n CLAUDE_MEM_TELEGRAM_ENABLED: string;\n CLAUDE_MEM_TELEGRAM_BOT_TOKEN: string;\n CLAUDE_MEM_TELEGRAM_CHAT_ID: string;\n CLAUDE_MEM_TELEGRAM_TRIGGER_TYPES: string;\n CLAUDE_MEM_TELEGRAM_TRIGGER_CONCEPTS: string;\n CLAUDE_MEM_QUEUE_ENGINE: string;\n CLAUDE_MEM_REDIS_URL: string;\n CLAUDE_MEM_REDIS_HOST: string;\n CLAUDE_MEM_REDIS_PORT: string;\n CLAUDE_MEM_REDIS_MODE: string;\n CLAUDE_MEM_QUEUE_REDIS_PREFIX: string;\n CLAUDE_MEM_AUTH_MODE: string;\n CLAUDE_MEM_RUNTIME: string;\n // Phase 1a (cmem-sdk rename): canonical server settings keys. Hooks read\n // these first and fall back to the legacy `*_BETA_*` keys below.\n CLAUDE_MEM_SERVER_URL: string;\n CLAUDE_MEM_SERVER_API_KEY: string;\n CLAUDE_MEM_SERVER_PROJECT_ID: string;\n // Legacy keys retained for back-compat with existing settings.json files.\n CLAUDE_MEM_SERVER_BETA_URL: string;\n CLAUDE_MEM_SERVER_BETA_API_KEY: string;\n CLAUDE_MEM_SERVER_BETA_PROJECT_ID: string;\n}\n\nexport class SettingsDefaultsManager {\n private static readonly DEFAULTS: SettingsDefaults = {\n CLAUDE_MEM_MODEL: 'claude-haiku-4-5-20251001',\n CLAUDE_MEM_CONTEXT_OBSERVATIONS: '50',\n CLAUDE_MEM_WORKER_PORT: String(37700 + ((process.getuid?.() ?? 77) % 100)),\n CLAUDE_MEM_WORKER_HOST: '127.0.0.1',\n CLAUDE_MEM_API_TIMEOUT_MS: String(getTimeout(HOOK_TIMEOUTS.API_REQUEST)),\n CLAUDE_MEM_SKIP_TOOLS: 'ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion',\n CLAUDE_MEM_PROVIDER: 'claude', // Default to Claude\n CLAUDE_MEM_CLAUDE_AUTH_METHOD: 'subscription', // Default to logged-in Claude SDK auth (not API key)\n CLAUDE_MEM_GEMINI_API_KEY: '', // Empty by default, can be set via UI or env\n CLAUDE_MEM_GEMINI_MODEL: 'gemini-2.5-flash-lite', // Default Gemini model (highest free tier RPM)\n CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED: 'true', // Rate limiting ON by default for free tier users\n CLAUDE_MEM_GEMINI_MAX_CONTEXT_MESSAGES: '20', // Max messages in Gemini context window\n CLAUDE_MEM_GEMINI_MAX_TOKENS: '100000', // Max estimated tokens (~100k safety limit)\n CLAUDE_MEM_OPENROUTER_API_KEY: '', // Empty by default, can be set via UI or env\n CLAUDE_MEM_OPENROUTER_MODEL: 'xiaomi/mimo-v2-flash:free', // Default OpenRouter model (free tier)\n CLAUDE_MEM_OPENROUTER_BASE_URL: '', // #2382/#2590/#2622/#2393 — optional OpenAI-compatible base URL (e.g. https://api.deepseek.com, http://localhost:1234/v1). Empty = default OpenRouter endpoint.\n CLAUDE_MEM_OPENROUTER_SITE_URL: '', // Optional: for OpenRouter analytics\n CLAUDE_MEM_OPENROUTER_APP_NAME: 'claude-mem', // App name for OpenRouter analytics\n CLAUDE_MEM_OPENROUTER_MAX_CONTEXT_MESSAGES: '20', // Max messages in context window\n CLAUDE_MEM_OPENROUTER_MAX_TOKENS: '100000', // Max estimated tokens (~100k safety limit)\n CLAUDE_MEM_DATA_DIR: join(homedir(), '.claude-mem'),\n CLAUDE_MEM_LOG_LEVEL: 'INFO',\n CLAUDE_MEM_PYTHON_VERSION: '3.13',\n CLAUDE_CODE_PATH: '', // Empty means auto-detect via 'which claude'\n CLAUDE_MEM_MODE: 'code', // Default mode profile\n CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: 'false',\n CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: 'false',\n CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: 'false',\n CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: 'true',\n CLAUDE_MEM_CONTEXT_FULL_COUNT: '0',\n CLAUDE_MEM_CONTEXT_FULL_FIELD: 'narrative',\n CLAUDE_MEM_CONTEXT_SESSION_COUNT: '10',\n CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: 'true',\n CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: 'false',\n CLAUDE_MEM_CONTEXT_SHOW_TERMINAL_OUTPUT: 'true',\n CLAUDE_MEM_WELCOME_HINT_ENABLED: 'true',\n CLAUDE_MEM_FOLDER_CLAUDEMD_ENABLED: 'false',\n CLAUDE_MEM_FOLDER_USE_LOCAL_MD: 'false', // When true, writes to CLAUDE.local.md instead of CLAUDE.md\n CLAUDE_MEM_TRANSCRIPTS_ENABLED: 'true',\n CLAUDE_MEM_TRANSCRIPTS_CONFIG_PATH: join(homedir(), '.claude-mem', 'transcript-watch.json'),\n CLAUDE_MEM_CODEX_TRANSCRIPT_INGESTION: 'false',\n CLAUDE_MEM_MAX_CONCURRENT_AGENTS: '2', // Max concurrent Claude SDK agent subprocesses\n CLAUDE_MEM_HOOK_FAIL_LOUD_THRESHOLD: '3', // Plan 05 Phase 8 — escalate to exit code 2 after N consecutive worker-unreachable hook invocations\n CLAUDE_MEM_EXCLUDED_PROJECTS: '', // Comma-separated glob patterns for excluded project paths\n CLAUDE_MEM_FOLDER_MD_EXCLUDE: '[]', // JSON array of folder paths to exclude from CLAUDE.md generation\n CLAUDE_MEM_FOLDER_MD_SKELETON_DENYLIST: '[]', // #2400 — JSON array of glob patterns; when a folder matches AND its generated CLAUDE.md would be empty/skeleton, skip injection (avoids polluting non-content dirs with empty skeletons). Default [] preserves existing behavior.\n CLAUDE_MEM_SEMANTIC_INJECT: 'false', // Inject relevant past observations on every UserPromptSubmit (experimental, disabled by default)\n CLAUDE_MEM_SEMANTIC_INJECT_LIMIT: '5', // Top-N most relevant observations to inject per prompt\n CLAUDE_MEM_TIER_ROUTING_ENABLED: 'true', // Route observations to models by complexity\n CLAUDE_MEM_TIER_SIMPLE_MODEL: 'haiku', // Portable tier alias — works across Direct API, Bedrock, Vertex, Azure (see #1463)\n CLAUDE_MEM_TIER_SUMMARY_MODEL: '', // Empty = use default model for summaries\n CLAUDE_MEM_TIER_FAST_MODEL: 'haiku', // #2289 — $TIER:fast resolves here (portable alias)\n CLAUDE_MEM_TIER_SMART_MODEL: 'sonnet', // #2289 — $TIER:smart resolves here (portable alias)\n CLAUDE_MEM_CHROMA_ENABLED: 'true', // Set to 'false' to disable Chroma and use SQLite-only search\n CLAUDE_MEM_CHROMA_MODE: 'local', // 'local' uses persistent chroma-mcp via uvx, 'remote' connects to existing server\n CLAUDE_MEM_CHROMA_HOST: '127.0.0.1',\n CLAUDE_MEM_CHROMA_PORT: '8000',\n CLAUDE_MEM_CHROMA_SSL: 'false',\n CLAUDE_MEM_CHROMA_API_KEY: '',\n CLAUDE_MEM_CHROMA_TENANT: 'default_tenant',\n CLAUDE_MEM_CHROMA_DATABASE: 'default_database',\n CLAUDE_MEM_TELEGRAM_ENABLED: 'true',\n CLAUDE_MEM_TELEGRAM_BOT_TOKEN: '',\n CLAUDE_MEM_TELEGRAM_CHAT_ID: '',\n CLAUDE_MEM_TELEGRAM_TRIGGER_TYPES: 'security_alert',\n CLAUDE_MEM_TELEGRAM_TRIGGER_CONCEPTS: '',\n CLAUDE_MEM_QUEUE_ENGINE: 'sqlite',\n CLAUDE_MEM_REDIS_URL: '',\n CLAUDE_MEM_REDIS_HOST: '127.0.0.1',\n CLAUDE_MEM_REDIS_PORT: '6379',\n CLAUDE_MEM_REDIS_MODE: 'external',\n CLAUDE_MEM_QUEUE_REDIS_PREFIX: `claude_mem_${process.env.CLAUDE_MEM_WORKER_PORT ?? String(37700 + ((process.getuid?.() ?? 77) % 100))}`,\n CLAUDE_MEM_AUTH_MODE: 'api-key',\n CLAUDE_MEM_RUNTIME: 'worker',\n // Phase 1a (cmem-sdk rename): canonical server settings keys. Hooks read\n // these first; the legacy `*_BETA_*` defaults below remain so existing\n // settings.json files still resolve correctly.\n CLAUDE_MEM_SERVER_URL: `http://127.0.0.1:${process.env.CLAUDE_MEM_SERVER_PORT ?? String(37877 + ((process.getuid?.() ?? 77) % 100))}`, // Default server runtime URL — UID-derived for multi-account isolation\n CLAUDE_MEM_SERVER_API_KEY: '', // Local hook API key, populated by installer when runtime=server\n CLAUDE_MEM_SERVER_PROJECT_ID: '', // Default Postgres project_id used by hooks when runtime=server\n CLAUDE_MEM_SERVER_BETA_URL: `http://127.0.0.1:${process.env.CLAUDE_MEM_SERVER_PORT ?? String(37877 + ((process.getuid?.() ?? 77) % 100))}`, // Legacy server-beta runtime URL — UID-derived for multi-account isolation\n CLAUDE_MEM_SERVER_BETA_API_KEY: '', // Legacy local hook API key (read as fallback when CLAUDE_MEM_SERVER_API_KEY unset)\n CLAUDE_MEM_SERVER_BETA_PROJECT_ID: '', // Legacy Postgres project_id (read as fallback when CLAUDE_MEM_SERVER_PROJECT_ID unset)\n };\n\n static getAllDefaults(): SettingsDefaults {\n return { ...this.DEFAULTS };\n }\n\n static get(key: keyof SettingsDefaults): string {\n return process.env[key] ?? this.DEFAULTS[key];\n }\n\n static getInt(key: keyof SettingsDefaults): number {\n const value = this.get(key);\n return parseInt(value, 10);\n }\n\n static getBool(key: keyof SettingsDefaults): boolean {\n const value: unknown = this.get(key);\n return value === 'true' || value === true;\n }\n\n private static applyEnvOverrides(settings: SettingsDefaults): SettingsDefaults {\n const result = { ...settings };\n for (const key of Object.keys(this.DEFAULTS) as Array<keyof SettingsDefaults>) {\n if (process.env[key] !== undefined) {\n result[key] = process.env[key]!;\n }\n }\n return result;\n }\n\n static loadFromFile(settingsPath: string, applyEnvOverrides = true): SettingsDefaults {\n try {\n if (!existsSync(settingsPath)) {\n const defaults = this.getAllDefaults();\n try {\n const dir = dirname(settingsPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(settingsPath, JSON.stringify(defaults, null, 2), 'utf-8');\n // stderr, never stdout: this fires on the first boot in a fresh data\n // dir, and CLI commands like `start` promise machine-readable JSON\n // on stdout to the hook framework.\n console.warn('[SETTINGS] Created settings file with defaults:', settingsPath);\n } catch (error: unknown) {\n console.warn('[SETTINGS] Failed to create settings file, using in-memory defaults:', settingsPath, error instanceof Error ? error.message : String(error));\n }\n return applyEnvOverrides ? this.applyEnvOverrides(defaults) : defaults;\n }\n\n const settingsData = readFileSync(settingsPath, 'utf-8');\n // Strip UTF-8 BOM if present — Windows tools (editors, formatters, CLI\n // hooks) may prepend U+FEFF which Bun's JSON.parse rejects silently,\n // causing a full fallback to defaults and breaking server-beta routing.\n const settings = JSON.parse(settingsData.replace(/^\\uFEFF/, ''));\n\n let flatSettings = settings;\n if (settings.env && typeof settings.env === 'object') {\n flatSettings = settings.env;\n\n try {\n writeFileSync(settingsPath, JSON.stringify(flatSettings, null, 2), 'utf-8');\n // stderr, never stdout — same JSON-on-stdout contract as above.\n console.warn('[SETTINGS] Migrated settings file from nested to flat schema:', settingsPath);\n } catch (error: unknown) {\n console.warn('[SETTINGS] Failed to auto-migrate settings file:', settingsPath, error instanceof Error ? error.message : String(error));\n // Continue with in-memory migration even if write fails\n }\n }\n\n const result: SettingsDefaults = { ...this.DEFAULTS };\n for (const key of Object.keys(this.DEFAULTS) as Array<keyof SettingsDefaults>) {\n if (flatSettings[key] !== undefined) {\n result[key] = flatSettings[key];\n }\n }\n\n return applyEnvOverrides ? this.applyEnvOverrides(result) : result;\n } catch (error: unknown) {\n console.warn('[SETTINGS] Failed to load settings, using defaults:', settingsPath, error instanceof Error ? error.message : String(error));\n const defaults = this.getAllDefaults();\n return applyEnvOverrides ? this.applyEnvOverrides(defaults) : defaults;\n }\n }\n}\n","export const HOOK_TIMEOUTS = {\n DEFAULT: 300000, // Standard HTTP timeout (5 min for slow systems)\n HEALTH_CHECK: 3000, // Worker health check (3s — healthy worker responds in <100ms)\n API_REQUEST: 30000, // Hook API calls should outlive health probes but stay below hook caps\n HOOK_READINESS_WAIT: 10000, // Per-hook wait for an already-starting worker to finish DB/search init\n POST_SPAWN_WAIT: 15000, // Wait for daemon to start after spawn (starts in <1s on Linux, 6-8s on macOS with Chroma)\n READINESS_WAIT: 30000, // Wait for DB + search init after spawn (typically <5s)\n PORT_IN_USE_WAIT: 3000, // Wait when port occupied but health failing\n WORKER_STARTUP_WAIT: 1000,\n PRE_RESTART_SETTLE_DELAY: 2000, // Give files time to sync before restart\n POWERSHELL_COMMAND: 10000, // PowerShell process enumeration (10s - typically completes in <1s)\n WINDOWS_MULTIPLIER: 1.5 \n} as const;\n\nexport const HOOK_EXIT_CODES = {\n SUCCESS: 0,\n FAILURE: 1,\n BLOCKING_ERROR: 2,\n USER_MESSAGE_ONLY: 3,\n} as const;\n\nexport function getTimeout(baseTimeout: number): number {\n return process.platform === 'win32'\n ? Math.round(baseTimeout * HOOK_TIMEOUTS.WINDOWS_MULTIPLIER)\n : baseTimeout;\n}\n","// Filters CLAUDE_CODE_* (and CLAUDECODE_*) unless explicitly preserved in\n// ENV_PRESERVE. This is layer 2 of defense for #2357 (CLAUDE_CODE_EFFORT_LEVEL\n// / CLAUDE_CODE_ALWAYS_ENABLE_EFFORT leaking into the SDK subprocess) — layer 1\n// is BLOCKED_ENV_VARS in EnvManager.ts. Do NOT add the EFFORT_* vars to\n// ENV_PRESERVE: preserving them would defeat the strip.\nexport const ENV_PREFIXES = ['CLAUDECODE_', 'CLAUDE_CODE_'];\nexport const ENV_EXACT_MATCHES = new Set([\n 'CLAUDECODE',\n 'CLAUDE_CODE_SESSION',\n 'CLAUDE_CODE_ENTRYPOINT',\n 'MCP_SESSION_ID',\n // Proxy vars: strip so the SDK subprocess doesn't inherit a proxy config.\n 'HTTP_PROXY',\n 'HTTPS_PROXY',\n 'ALL_PROXY',\n 'NO_PROXY',\n 'http_proxy',\n 'https_proxy',\n 'all_proxy',\n 'no_proxy',\n 'npm_config_proxy',\n 'npm_config_https_proxy',\n]);\n\nexport const ENV_PRESERVE = new Set([\n 'CLAUDE_CODE_OAUTH_TOKEN',\n 'CLAUDE_CODE_GIT_BASH_PATH',\n 'CLAUDE_CODE_USE_BEDROCK',\n 'CLAUDE_CODE_USE_VERTEX',\n 'ANTHROPIC_BEDROCK_BASE_URL',\n 'AWS_REGION',\n 'AWS_PROFILE',\n 'AWS_ACCESS_KEY_ID',\n 'AWS_SECRET_ACCESS_KEY',\n 'AWS_SESSION_TOKEN',\n 'ANTHROPIC_VERTEX_PROJECT_ID',\n 'CLOUD_ML_REGION',\n 'GOOGLE_APPLICATION_CREDENTIALS',\n]);\n\nexport function sanitizeEnv(env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {\n const sanitized: NodeJS.ProcessEnv = {};\n\n for (const [key, value] of Object.entries(env)) {\n if (value === undefined) continue;\n if (ENV_PRESERVE.has(key)) { sanitized[key] = value; continue; }\n if (ENV_EXACT_MATCHES.has(key)) continue;\n if (ENV_PREFIXES.some(prefix => key.startsWith(prefix))) continue;\n sanitized[key] = value;\n }\n\n return sanitized;\n}\n","import { existsSync, readFileSync, rmSync } from 'fs';\nimport { logger } from '../utils/logger.js';\nimport {\n getProcessRegistry,\n verifyPidFileOwnership,\n type ManagedProcessInfo,\n type PidInfo,\n type ProcessRegistry\n} from './process-registry.js';\nimport { runShutdownCascade } from './shutdown.js';\nimport { startHealthChecker, stopHealthChecker } from './health-checker.js';\nimport { paths } from '../shared/paths.js';\n\nconst PID_FILE = paths.workerPid();\n\ninterface ValidateWorkerPidOptions {\n logAlive?: boolean;\n pidFilePath?: string;\n}\n\nexport type ValidateWorkerPidStatus = 'missing' | 'alive' | 'stale' | 'invalid';\n\nclass Supervisor {\n private readonly registry: ProcessRegistry;\n private started = false;\n private stopPromise: Promise<void> | null = null;\n private signalHandlersRegistered = false;\n private shutdownInitiated = false;\n private shutdownHandler: (() => Promise<void>) | null = null;\n\n constructor(registry: ProcessRegistry) {\n this.registry = registry;\n }\n\n async start(): Promise<void> {\n if (this.started) return;\n\n this.registry.initialize();\n const pidStatus = validateWorkerPidFile({ logAlive: false });\n if (pidStatus === 'alive') {\n throw new Error('Worker already running');\n }\n\n this.started = true;\n\n startHealthChecker();\n }\n\n configureSignalHandlers(shutdownHandler: () => Promise<void>): void {\n this.shutdownHandler = shutdownHandler;\n\n if (this.signalHandlersRegistered) return;\n this.signalHandlersRegistered = true;\n\n const handleSignal = async (signal: string): Promise<void> => {\n if (this.shutdownInitiated) {\n logger.warn('SYSTEM', `Received ${signal} but shutdown already in progress`);\n return;\n }\n this.shutdownInitiated = true;\n\n logger.info('SYSTEM', `Received ${signal}, shutting down...`);\n\n try {\n if (this.shutdownHandler) {\n await this.shutdownHandler();\n } else {\n await this.stop();\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.error('SYSTEM', 'Error during shutdown', {}, error);\n } else {\n logger.error('SYSTEM', 'Error during shutdown (non-Error)', { error: String(error) });\n }\n try {\n await this.stop();\n } catch (stopError: unknown) {\n if (stopError instanceof Error) {\n logger.debug('SYSTEM', 'Supervisor shutdown fallback failed', {}, stopError);\n } else {\n logger.debug('SYSTEM', 'Supervisor shutdown fallback failed', { error: String(stopError) });\n }\n }\n }\n\n process.exit(0);\n };\n\n process.on('SIGTERM', () => void handleSignal('SIGTERM'));\n process.on('SIGINT', () => void handleSignal('SIGINT'));\n\n if (process.platform !== 'win32') {\n if (process.argv.includes('--daemon')) {\n process.on('SIGHUP', () => {\n logger.debug('SYSTEM', 'Ignoring SIGHUP in daemon mode');\n });\n } else {\n process.on('SIGHUP', () => void handleSignal('SIGHUP'));\n }\n }\n }\n\n async stop(): Promise<void> {\n if (this.stopPromise) {\n await this.stopPromise;\n return;\n }\n\n stopHealthChecker();\n this.stopPromise = runShutdownCascade({\n registry: this.registry,\n currentPid: process.pid\n }).finally(() => {\n this.started = false;\n this.stopPromise = null;\n });\n\n await this.stopPromise;\n }\n\n assertCanSpawn(type: string): void {\n if (this.stopPromise !== null) {\n throw new Error(`Supervisor is shutting down, refusing to spawn ${type}`);\n }\n }\n\n registerProcess(id: string, processInfo: ManagedProcessInfo, processRef?: Parameters<ProcessRegistry['register']>[2]): void {\n this.registry.register(id, processInfo, processRef);\n }\n\n unregisterProcess(id: string): void {\n this.registry.unregister(id);\n }\n\n getRegistry(): ProcessRegistry {\n return this.registry;\n }\n}\n\nconst supervisorSingleton = new Supervisor(getProcessRegistry());\n\nexport async function startSupervisor(): Promise<void> {\n await supervisorSingleton.start();\n}\n\nexport function getSupervisor(): Supervisor {\n return supervisorSingleton;\n}\n\nexport function configureSupervisorSignalHandlers(shutdownHandler: () => Promise<void>): void {\n supervisorSingleton.configureSignalHandlers(shutdownHandler);\n}\n\nexport function validateWorkerPidFile(options: ValidateWorkerPidOptions = {}): ValidateWorkerPidStatus {\n const pidFilePath = options.pidFilePath ?? PID_FILE;\n\n if (!existsSync(pidFilePath)) {\n return 'missing';\n }\n\n let pidInfo: PidInfo | null = null;\n\n try {\n pidInfo = JSON.parse(readFileSync(pidFilePath, 'utf-8')) as PidInfo | null;\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.warn('SYSTEM', 'Failed to parse worker PID file, removing it', { path: pidFilePath }, error);\n } else {\n logger.warn('SYSTEM', 'Failed to parse worker PID file, removing it', {\n path: pidFilePath,\n error: String(error)\n });\n }\n rmSync(pidFilePath, { force: true });\n return 'invalid';\n }\n\n const isAlive = verifyPidFileOwnership(pidInfo);\n if (isAlive && pidInfo) {\n if (options.logAlive ?? true) {\n logger.info('SYSTEM', 'Worker already running (PID alive)', {\n existingPid: pidInfo.pid,\n existingPort: pidInfo.port,\n startedAt: pidInfo.startedAt\n });\n }\n return 'alive';\n }\n\n logger.info('SYSTEM', 'Removing stale PID file (worker process is dead or PID has been reused)', {\n pid: pidInfo?.pid,\n port: pidInfo?.port,\n startedAt: pidInfo?.startedAt\n });\n rmSync(pidFilePath, { force: true });\n return 'stale';\n}\n","import { ChildProcess, spawnSync } from 'child_process';\nimport { spawnHidden } from '../shared/spawn.js';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport path from 'path';\nimport { logger } from '../utils/logger.js';\nimport { sanitizeEnv } from './env-sanitizer.js';\nimport { paths } from '../shared/paths.js';\n\nconst REAP_SESSION_SIGTERM_TIMEOUT_MS = 5_000;\nconst REAP_SESSION_SIGKILL_TIMEOUT_MS = 1_000;\n\nconst DEFAULT_REGISTRY_PATH = paths.supervisorRegistry();\n\nexport interface ManagedProcessInfo {\n pid: number;\n type: string;\n sessionId?: string | number;\n startedAt: string;\n pgid?: number;\n}\n\nexport interface ManagedProcessRecord extends ManagedProcessInfo {\n id: string;\n}\n\ninterface PersistedRegistry {\n processes: Record<string, ManagedProcessInfo>;\n}\n\nexport function isPidAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid < 0) return false;\n if (pid === 0) return false;\n\n try {\n process.kill(pid, 0);\n return true;\n } catch (error: unknown) {\n if (error instanceof Error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === 'EPERM') return true;\n logger.debug('SYSTEM', 'PID check failed', { pid, code });\n return false;\n }\n logger.warn('SYSTEM', 'PID check threw non-Error', { pid, error: String(error) });\n return false;\n }\n}\n\n// Poll until every record's pid is gone or the timeout elapses. Shared by the\n// reapSession wait phase and shutdown.ts's SIGTERM grace period.\nexport async function waitForExit(records: ManagedProcessRecord[], timeoutMs: number): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (records.every(record => !isPidAlive(record.pid))) {\n return;\n }\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n}\n\nexport interface PidInfo {\n pid: number;\n port: number;\n startedAt: string;\n startToken?: string;\n}\n\n// Windows lacks a cheap /proc-style start-time read and `ps lstart`, so we\n// shell to PowerShell's CIM (wmic is removed on Windows 11). The lookup is\n// ~100-300ms, so cache per-pid for 5s to avoid re-shelling when the same PID\n// is validated repeatedly within one spawn-decision window.\nconst WINDOWS_START_TOKEN_CACHE_TTL_MS = 5_000;\nconst windowsStartTokenCache = new Map<number, { token: string | null; capturedAtMs: number }>();\n\nfunction captureWindowsStartToken(pid: number): string | null {\n const cached = windowsStartTokenCache.get(pid);\n if (cached && Date.now() - cached.capturedAtMs < WINDOWS_START_TOKEN_CACHE_TTL_MS) {\n return cached.token;\n }\n\n let token: string | null = null;\n try {\n // CreationDate is a CIM DATETIME (yyyyMMddHHmmss.ffffff±UTCoffset) that is\n // unique-enough per (pid, boot) to detect PID reuse. `-NoProfile` keeps it\n // fast; sanitizeEnv keeps the spawn-env discipline uniform (#2357/#2375).\n const result = spawnSync(\n 'powershell.exe',\n [\n '-NoProfile',\n '-NonInteractive',\n '-Command',\n `(Get-CimInstance Win32_Process -Filter \\\"ProcessId=${pid}\\\").CreationDate.ToString('yyyyMMddHHmmss.ffffff')`\n ],\n {\n encoding: 'utf-8',\n timeout: 5000,\n windowsHide: true,\n env: { ...sanitizeEnv(process.env), LC_ALL: 'C', LANG: 'C' }\n }\n );\n if (result.status === 0) {\n const trimmed = result.stdout.trim();\n token = trimmed.length > 0 ? trimmed : null;\n }\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'captureProcessStartToken: powershell CIM lookup failed', {\n pid,\n error: error instanceof Error ? error.message : String(error)\n });\n token = null;\n }\n\n windowsStartTokenCache.set(pid, { token, capturedAtMs: Date.now() });\n return token;\n}\n\nexport function captureProcessStartToken(pid: number): string | null {\n if (!Number.isInteger(pid) || pid <= 0) return null;\n\n if (process.platform === 'linux') {\n try {\n const raw = readFileSync(`/proc/${pid}/stat`, 'utf-8');\n const tailStart = raw.lastIndexOf(') ');\n if (tailStart < 0) return null;\n const fields = raw.slice(tailStart + 2).split(' ');\n const starttime = fields[19];\n return starttime && /^\\d+$/.test(starttime) ? starttime : null;\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'captureProcessStartToken: /proc read failed', {\n pid,\n error: error instanceof Error ? error.message : String(error)\n });\n return null;\n }\n }\n\n if (process.platform === 'win32') {\n return captureWindowsStartToken(pid);\n }\n\n try {\n const result = spawnSync('ps', ['-p', String(pid), '-o', 'lstart='], {\n encoding: 'utf-8',\n timeout: 2000,\n // Uniform spawn-env discipline: sanitize even for read-only system\n // binaries so the spawn-env CI check stays a single rule (#2357/#2375).\n env: { ...sanitizeEnv(process.env), LC_ALL: 'C', LANG: 'C' }\n });\n if (result.status !== 0) return null;\n const token = result.stdout.trim();\n return token.length > 0 ? token : null;\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'captureProcessStartToken: ps exec failed', {\n pid,\n error: error instanceof Error ? error.message : String(error)\n });\n return null;\n }\n}\n\nexport function verifyPidFileOwnership(info: PidInfo | null): info is PidInfo {\n if (!info) return false;\n if (!isPidAlive(info.pid)) return false;\n\n if (!info.startToken) return true;\n\n const currentToken = captureProcessStartToken(info.pid);\n if (currentToken === null) return true;\n\n const match = currentToken === info.startToken;\n if (!match) {\n logger.debug('SYSTEM', 'verifyPidFileOwnership: start-token mismatch (PID reused)', {\n pid: info.pid,\n stored: info.startToken,\n current: currentToken\n });\n }\n return match;\n}\n\nexport class ProcessRegistry {\n private readonly registryPath: string;\n private readonly entries = new Map<string, ManagedProcessInfo>();\n private readonly runtimeProcesses = new Map<string, ChildProcess>();\n private initialized = false;\n\n constructor(registryPath: string = DEFAULT_REGISTRY_PATH) {\n this.registryPath = registryPath;\n }\n\n initialize(): void {\n if (this.initialized) return;\n this.initialized = true;\n\n mkdirSync(path.dirname(this.registryPath), { recursive: true });\n\n if (!existsSync(this.registryPath)) {\n this.persist();\n return;\n }\n\n try {\n const raw = JSON.parse(readFileSync(this.registryPath, 'utf-8')) as PersistedRegistry;\n const processes = raw.processes ?? {};\n for (const [id, info] of Object.entries(processes)) {\n this.entries.set(id, info);\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.warn('SYSTEM', 'Failed to parse supervisor registry, rebuilding', {\n path: this.registryPath\n }, error);\n } else {\n logger.warn('SYSTEM', 'Failed to parse supervisor registry, rebuilding', {\n path: this.registryPath,\n error: String(error)\n });\n }\n this.entries.clear();\n }\n\n const removed = this.pruneDeadEntries();\n if (removed > 0) {\n logger.info('SYSTEM', 'Removed dead processes from supervisor registry', { removed });\n }\n this.persist();\n }\n\n register(id: string, processInfo: ManagedProcessInfo, processRef?: ChildProcess): void {\n this.initialize();\n this.entries.set(id, processInfo);\n if (processRef) {\n this.runtimeProcesses.set(id, processRef);\n }\n this.persist();\n }\n\n unregister(id: string): void {\n this.initialize();\n const existing = this.entries.get(id);\n this.entries.delete(id);\n this.runtimeProcesses.delete(id);\n this.persist();\n if (existing?.type === 'sdk') notifySlotAvailable();\n }\n\n clear(): void {\n this.entries.clear();\n this.runtimeProcesses.clear();\n this.persist();\n }\n\n getAll(): ManagedProcessRecord[] {\n this.initialize();\n return Array.from(this.entries.entries())\n .map(([id, info]) => ({ id, ...info }))\n .sort((a, b) => {\n const left = Date.parse(a.startedAt);\n const right = Date.parse(b.startedAt);\n return (Number.isNaN(left) ? 0 : left) - (Number.isNaN(right) ? 0 : right);\n });\n }\n\n getBySession(sessionId: string | number): ManagedProcessRecord[] {\n const normalized = String(sessionId);\n return this.getAll().filter(record => record.sessionId !== undefined && String(record.sessionId) === normalized);\n }\n\n getRuntimeProcess(id: string): ChildProcess | undefined {\n return this.runtimeProcesses.get(id);\n }\n\n getByPid(pid: number): ManagedProcessRecord[] {\n return this.getAll().filter(record => record.pid === pid);\n }\n\n pruneDeadEntries(): number {\n this.initialize();\n\n let removed = 0;\n let removedSdk = 0;\n for (const [id, info] of this.entries) {\n if (isPidAlive(info.pid)) continue;\n this.entries.delete(id);\n this.runtimeProcesses.delete(id);\n removed += 1;\n if (info.type === 'sdk') removedSdk += 1;\n }\n\n if (removed > 0) {\n this.persist();\n }\n for (let i = 0; i < removedSdk; i += 1) notifySlotAvailable();\n\n return removed;\n }\n\n async reapSession(sessionId: string | number): Promise<number> {\n this.initialize();\n\n const sessionRecords = this.getBySession(sessionId);\n if (sessionRecords.length === 0) {\n return 0;\n }\n\n const sessionIdNum = typeof sessionId === 'number' ? sessionId : Number(sessionId) || undefined;\n logger.info('SYSTEM', `Reaping ${sessionRecords.length} process(es) for session ${sessionId}`, {\n sessionId: sessionIdNum,\n pids: sessionRecords.map(r => r.pid)\n });\n\n const aliveRecords = sessionRecords.filter(r => isPidAlive(r.pid));\n for (const record of aliveRecords) {\n try {\n if (typeof record.pgid === 'number' && process.platform !== 'win32') {\n process.kill(-record.pgid, 'SIGTERM');\n } else {\n process.kill(record.pid, 'SIGTERM');\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== 'ESRCH') {\n logger.debug('SYSTEM', `Failed to SIGTERM session process PID ${record.pid}`, {\n pid: record.pid,\n pgid: record.pgid\n }, error);\n }\n } else {\n logger.warn('SYSTEM', `Failed to SIGTERM session process PID ${record.pid} (non-Error)`, {\n pid: record.pid,\n pgid: record.pgid,\n error: String(error)\n });\n }\n }\n }\n\n await waitForExit(aliveRecords, REAP_SESSION_SIGTERM_TIMEOUT_MS);\n\n const survivors = aliveRecords.filter(r => isPidAlive(r.pid));\n for (const record of survivors) {\n logger.warn('SYSTEM', `Session process PID ${record.pid} did not exit after SIGTERM, sending SIGKILL`, {\n pid: record.pid,\n pgid: record.pgid,\n sessionId: sessionIdNum\n });\n try {\n if (typeof record.pgid === 'number' && process.platform !== 'win32') {\n process.kill(-record.pgid, 'SIGKILL');\n } else {\n process.kill(record.pid, 'SIGKILL');\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== 'ESRCH') {\n logger.debug('SYSTEM', `Failed to SIGKILL session process PID ${record.pid}`, {\n pid: record.pid,\n pgid: record.pgid\n }, error);\n }\n } else {\n logger.warn('SYSTEM', `Failed to SIGKILL session process PID ${record.pid} (non-Error)`, {\n pid: record.pid,\n pgid: record.pgid,\n error: String(error)\n });\n }\n }\n }\n\n if (survivors.length > 0) {\n const sigkillDeadline = Date.now() + REAP_SESSION_SIGKILL_TIMEOUT_MS;\n while (Date.now() < sigkillDeadline) {\n const remaining = survivors.filter(r => isPidAlive(r.pid));\n if (remaining.length === 0) break;\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n }\n\n for (const record of sessionRecords) {\n this.entries.delete(record.id);\n this.runtimeProcesses.delete(record.id);\n }\n this.persist();\n for (const record of sessionRecords) {\n if (record.type === 'sdk') notifySlotAvailable();\n }\n\n logger.info('SYSTEM', `Reaped ${sessionRecords.length} process(es) for session ${sessionId}`, {\n sessionId: sessionIdNum,\n reaped: sessionRecords.length\n });\n\n return sessionRecords.length;\n }\n\n private persist(): void {\n const payload: PersistedRegistry = {\n processes: Object.fromEntries(this.entries.entries())\n };\n\n mkdirSync(path.dirname(this.registryPath), { recursive: true });\n writeFileSync(this.registryPath, JSON.stringify(payload, null, 2));\n }\n}\n\nlet registrySingleton: ProcessRegistry | null = null;\n\nexport function getProcessRegistry(): ProcessRegistry {\n if (!registrySingleton) {\n registrySingleton = new ProcessRegistry();\n }\n return registrySingleton;\n}\n\nexport function createProcessRegistry(registryPath: string): ProcessRegistry {\n return new ProcessRegistry(registryPath);\n}\n\nexport interface TrackedSdkProcess {\n pid: number;\n pgid: number | undefined;\n sessionDbId: number;\n process: ChildProcess;\n}\n\nexport function getSdkProcessForSession(sessionDbId: number): TrackedSdkProcess | undefined {\n const registry = getProcessRegistry();\n const matches = registry.getBySession(sessionDbId).filter(r => r.type === 'sdk');\n\n if (matches.length > 1) {\n logger.warn('PROCESS', `Multiple SDK processes found for session ${sessionDbId}`, {\n count: matches.length,\n pids: matches.map(m => m.pid),\n });\n }\n\n const record = matches[0];\n if (!record) return undefined;\n\n const processRef = registry.getRuntimeProcess(record.id);\n if (!processRef) return undefined;\n\n return {\n pid: record.pid,\n pgid: record.pgid,\n sessionDbId,\n process: processRef,\n };\n}\n\nexport async function ensureSdkProcessExit(\n tracked: TrackedSdkProcess,\n timeoutMs: number = 5000\n): Promise<void> {\n const { pid, pgid, process: proc } = tracked;\n\n if (proc.exitCode !== null) return;\n\n const exitPromise = new Promise<void>((resolve) => {\n proc.once('exit', () => resolve());\n });\n\n const timeoutPromise = new Promise<void>((resolve) => {\n setTimeout(resolve, timeoutMs);\n });\n\n await Promise.race([exitPromise, timeoutPromise]);\n\n if (proc.exitCode !== null) return;\n\n logger.warn('PROCESS', `PID ${pid} did not exit after ${timeoutMs}ms, sending SIGKILL to process group`, {\n pid, pgid, timeoutMs,\n });\n try {\n if (typeof pgid === 'number' && process.platform !== 'win32') {\n process.kill(-pgid, 'SIGKILL');\n } else {\n proc.kill('SIGKILL');\n }\n } catch {\n // Already dead — fine.\n }\n\n const sigkillExit = new Promise<void>((resolve) => {\n proc.once('exit', () => resolve());\n });\n const sigkillTimeout = new Promise<void>((resolve) => {\n setTimeout(resolve, 1000);\n });\n await Promise.race([sigkillExit, sigkillTimeout]);\n}\n\nconst TOTAL_PROCESS_HARD_CAP = 10;\nconst SLOT_RECHECK_INTERVAL_MS = 5_000;\nconst slotWaiters: Array<() => void> = [];\n\nfunction getActiveSdkCount(): number {\n return getProcessRegistry().getAll().filter(record => record.type === 'sdk').length;\n}\n\nfunction notifySlotAvailable(): void {\n const waiter = slotWaiters.shift();\n if (waiter) waiter();\n}\n\nexport async function waitForSlot(maxConcurrent: number, signal?: AbortSignal): Promise<void> {\n getProcessRegistry().pruneDeadEntries();\n const activeCount = getActiveSdkCount();\n if (activeCount >= TOTAL_PROCESS_HARD_CAP) {\n throw new Error(`Hard cap exceeded: ${activeCount} processes in registry (cap=${TOTAL_PROCESS_HARD_CAP}). Refusing to spawn more.`);\n }\n\n if (activeCount < maxConcurrent) return;\n\n if (signal?.aborted) {\n throw new Error('waitForSlot aborted before queuing');\n }\n\n logger.info('PROCESS', `Pool limit reached (${activeCount}/${maxConcurrent}), waiting for slot...`);\n\n return new Promise<void>((resolve, reject) => {\n let recheckTimer: ReturnType<typeof setInterval> | null = null;\n let abortHandler: (() => void) | null = null;\n const cleanup = () => {\n if (recheckTimer) clearInterval(recheckTimer);\n if (abortHandler && signal) signal.removeEventListener('abort', abortHandler);\n const idx = slotWaiters.indexOf(onSlot);\n if (idx >= 0) slotWaiters.splice(idx, 1);\n };\n const onSlot = () => {\n const count = getActiveSdkCount();\n if (count >= TOTAL_PROCESS_HARD_CAP) {\n cleanup();\n reject(new Error(`Hard cap exceeded: ${count} processes in registry (cap=${TOTAL_PROCESS_HARD_CAP}). Refusing to spawn more.`));\n return;\n }\n\n if (count < maxConcurrent) {\n cleanup();\n resolve();\n } else {\n slotWaiters.push(onSlot);\n }\n };\n\n if (signal) {\n abortHandler = () => {\n cleanup();\n reject(new Error('waitForSlot aborted'));\n };\n signal.addEventListener('abort', abortHandler, { once: true });\n }\n\n slotWaiters.push(onSlot);\n recheckTimer = setInterval(() => {\n const removed = getProcessRegistry().pruneDeadEntries();\n if (removed > 0) {\n logger.info('PROCESS', 'Pruned stale process registry entries while waiting for agent slot', { removed });\n return;\n }\n notifySlotAvailable();\n }, SLOT_RECHECK_INTERVAL_MS);\n recheckTimer.unref?.();\n });\n}\n\nexport interface SpawnedSdkProcess {\n stdin: NonNullable<ChildProcess['stdin']>;\n stdout: NonNullable<ChildProcess['stdout']>;\n stderr: NonNullable<ChildProcess['stderr']>;\n readonly killed: boolean;\n readonly exitCode: number | null;\n kill: ChildProcess['kill'];\n on: ChildProcess['on'];\n once: ChildProcess['once'];\n off: ChildProcess['off'];\n}\n\nexport interface SpawnSdkOptions {\n command: string;\n args: string[];\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n signal?: AbortSignal;\n}\n\nexport function spawnSdkProcess(\n sessionDbId: number,\n options: SpawnSdkOptions\n): { process: SpawnedSdkProcess; pid: number; pgid: number } | null {\n const registry = getProcessRegistry();\n\n const useCmdWrapper = process.platform === 'win32' && options.command.endsWith('.cmd');\n const env = sanitizeEnv(options.env ?? process.env);\n\n const filteredArgs: string[] = [];\n for (const arg of options.args) {\n if (arg === '') {\n if (filteredArgs.length > 0 && filteredArgs[filteredArgs.length - 1].startsWith('--')) {\n filteredArgs.pop();\n }\n continue;\n }\n filteredArgs.push(arg);\n }\n\n const isWin = process.platform === 'win32';\n const child = useCmdWrapper\n ? spawnHidden('cmd.exe', ['/d', '/c', options.command, ...filteredArgs], {\n cwd: options.cwd,\n env,\n detached: !isWin,\n stdio: ['pipe', 'pipe', 'pipe'],\n signal: options.signal,\n windowsHide: true,\n })\n : spawnHidden(options.command, filteredArgs, {\n cwd: options.cwd,\n env,\n detached: !isWin,\n stdio: ['pipe', 'pipe', 'pipe'],\n signal: options.signal,\n windowsHide: true,\n });\n\n child.on('error', (err: Error) => {\n logger.warn('SDK_SPAWN', `[session-${sessionDbId}] child emitted error event`, {\n sessionDbId,\n pid: child.pid,\n errorName: err.name,\n errorCode: (err as NodeJS.ErrnoException).code,\n }, err);\n });\n\n if (!child.pid) {\n logger.error('PROCESS', 'Spawn succeeded but produced no PID', { sessionDbId });\n return null;\n }\n\n const pid = child.pid;\n const pgid = pid; \n\n // Keep the tail of stderr so a non-zero exit can say WHY at WARN level.\n // Without this, a CLI that dies at flag parsing (\"error: unknown option…\")\n // logs only an opaque {code=1} and the real cause is invisible unless the\n // worker happens to run at DEBUG.\n const STDERR_TAIL_MAX_CHARS = 2048;\n let stderrTail = '';\n if (child.stderr) {\n child.stderr.on('data', (data: Buffer) => {\n const text = data.toString();\n stderrTail = (stderrTail + text).slice(-STDERR_TAIL_MAX_CHARS);\n logger.debug('SDK_SPAWN', `[session-${sessionDbId}] stderr: ${text.trim()}`);\n });\n }\n\n const recordId = `sdk:${sessionDbId}:${pid}`;\n registry.register(recordId, {\n pid,\n type: 'sdk',\n sessionId: sessionDbId,\n startedAt: new Date().toISOString(),\n pgid,\n }, child);\n\n child.on('exit', () => {\n registry.unregister(recordId);\n });\n\n // 'close', not 'exit': 'exit' can fire while piped stderr still holds\n // buffered data, truncating the tail. 'close' waits for all stdio to drain.\n child.on('close', (code: number | null, signal: string | null) => {\n if (code !== 0) {\n const tail = stderrTail.trim();\n logger.warn('SDK_SPAWN', `[session-${sessionDbId}] Claude process exited`, {\n code,\n signal,\n pid,\n ...(tail ? { stderrTail: tail } : {}),\n });\n }\n });\n\n if (!child.stdin || !child.stdout || !child.stderr) {\n logger.error('PROCESS', 'Spawned SDK child missing required stdio streams', {\n sessionDbId,\n pid,\n hasStdin: Boolean(child.stdin),\n hasStdout: Boolean(child.stdout),\n hasStderr: Boolean(child.stderr),\n });\n try { child.kill('SIGKILL'); } catch { /* already dead */ }\n return null;\n }\n\n const spawned: SpawnedSdkProcess = {\n stdin: child.stdin,\n stdout: child.stdout,\n stderr: child.stderr,\n get killed() { return child.killed; },\n get exitCode() { return child.exitCode; },\n kill: child.kill.bind(child),\n on: child.on.bind(child),\n once: child.once.bind(child),\n off: child.off.bind(child),\n };\n\n return { process: spawned, pid, pgid };\n}\n\nexport function createSdkSpawnFactory(sessionDbId: number) {\n return (spawnOptions: SpawnSdkOptions): SpawnedSdkProcess => {\n const registry = getProcessRegistry();\n\n const existing = registry.getBySession(sessionDbId).filter(r => r.type === 'sdk');\n for (const record of existing) {\n if (!isPidAlive(record.pid)) continue;\n try {\n if (typeof record.pgid === 'number') {\n if (process.platform !== 'win32') {\n process.kill(-record.pgid, 'SIGTERM');\n } else {\n process.kill(record.pid, 'SIGTERM');\n }\n } else {\n process.kill(record.pid, 'SIGTERM');\n }\n logger.warn('PROCESS', `Killing duplicate SDK process PID ${record.pid} before spawning new one for session ${sessionDbId}`, {\n existingPid: record.pid,\n sessionDbId,\n });\n } catch (error: unknown) {\n const code = error instanceof Error ? (error as NodeJS.ErrnoException).code : undefined;\n if (code !== 'ESRCH') {\n if (error instanceof Error) {\n logger.warn('PROCESS', `Failed to SIGTERM duplicate SDK process PID ${record.pid}`, { sessionDbId }, error);\n } else {\n logger.warn('PROCESS', `Failed to SIGTERM duplicate SDK process PID ${record.pid} (non-Error)`, {\n sessionDbId, error: String(error),\n });\n }\n }\n }\n }\n\n const result = spawnSdkProcess(sessionDbId, spawnOptions);\n if (!result) {\n throw new Error(`Failed to spawn SDK subprocess for session ${sessionDbId}`);\n }\n\n return result.process;\n };\n}\n","// F1 foundation: spawn wrapper that hides child windows on Windows by default. See src/shared/spawn.ts.test.ts for invariant.\nimport { spawn, type SpawnOptions, type ChildProcess } from 'node:child_process';\n\nexport type SpawnHiddenOptions = SpawnOptions;\n\nexport function spawnHidden(\n command: string,\n args?: readonly string[],\n options?: SpawnOptions\n): ChildProcess {\n return spawn(command, args ?? [], { windowsHide: true, ...options });\n}\n","import { execFile } from 'child_process';\nimport { existsSync, readFileSync, rmSync } from 'fs';\nimport { promisify } from 'util';\nimport { logger } from '../utils/logger.js';\nimport { HOOK_TIMEOUTS } from '../shared/hook-constants.js';\nimport { isPidAlive, waitForExit, type ManagedProcessRecord, type ProcessRegistry } from './process-registry.js';\nimport { paths } from '../shared/paths.js';\n\nconst execFileAsync = promisify(execFile);\nconst PID_FILE = paths.workerPid();\n\ntype TreeKillFn = (pid: number, signal?: string, callback?: (error?: Error | null) => void) => void;\n\nexport interface ShutdownCascadeOptions {\n registry: ProcessRegistry;\n currentPid?: number;\n pidFilePath?: string;\n}\n\nexport async function runShutdownCascade(options: ShutdownCascadeOptions): Promise<void> {\n const currentPid = options.currentPid ?? process.pid;\n const pidFilePath = options.pidFilePath ?? PID_FILE;\n const allRecords = options.registry.getAll();\n const childRecords = [...allRecords]\n .filter(record => record.pid !== currentPid)\n .sort((a, b) => Date.parse(b.startedAt) - Date.parse(a.startedAt));\n\n for (const record of childRecords) {\n if (!isPidAlive(record.pid)) {\n options.registry.unregister(record.id);\n continue;\n }\n\n try {\n await signalProcess(record, 'SIGTERM');\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.debug('SYSTEM', 'Failed to send SIGTERM to child process', {\n pid: record.pid,\n pgid: record.pgid,\n type: record.type\n }, error);\n } else {\n logger.warn('SYSTEM', 'Failed to send SIGTERM to child process (non-Error)', {\n pid: record.pid,\n pgid: record.pgid,\n type: record.type,\n error: String(error)\n });\n }\n }\n }\n\n await waitForExit(childRecords, 5000);\n\n const survivors = childRecords.filter(record => isPidAlive(record.pid));\n for (const record of survivors) {\n try {\n await signalProcess(record, 'SIGKILL');\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.debug('SYSTEM', 'Failed to force kill child process', {\n pid: record.pid,\n pgid: record.pgid,\n type: record.type\n }, error);\n } else {\n logger.warn('SYSTEM', 'Failed to force kill child process (non-Error)', {\n pid: record.pid,\n pgid: record.pgid,\n type: record.type,\n error: String(error)\n });\n }\n }\n }\n\n await waitForExit(survivors, 1000);\n\n for (const record of childRecords) {\n options.registry.unregister(record.id);\n }\n for (const record of allRecords.filter(record => record.pid === currentPid)) {\n options.registry.unregister(record.id);\n }\n\n removeOwnedPidFile(pidFilePath, currentPid);\n\n options.registry.pruneDeadEntries();\n}\n\n/**\n * Owner-guarded PID-file removal (Phase 5, worker-restart plan).\n *\n * The shutdown cascade is the dying worker's LAST act — during a restart the\n * successor worker has typically already written its OWN PID file by the time\n * this runs. Blindly rmSync'ing here clobbered that file and made\n * `worker status` report a healthy worker as not running. Deletion therefore\n * requires proof of ownership: the recorded pid must equal `currentPid`.\n *\n * A missing file is a no-op. An unreadable/corrupt file cannot prove\n * ownership, so it is left in place (the safe default): readPidFile() and\n * validateWorkerPidFile() both treat unparseable files as ownerless, so a\n * leftover corrupt file never blocks a successor's boot and is cleaned up by\n * the next worker start.\n */\nexport function removeOwnedPidFile(pidFilePath: string, currentPid: number): void {\n if (!existsSync(pidFilePath)) return;\n\n let recordedPid: number | null = null;\n try {\n const parsed = JSON.parse(readFileSync(pidFilePath, 'utf-8')) as { pid?: unknown };\n recordedPid = typeof parsed.pid === 'number' ? parsed.pid : null;\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'PID file unreadable during shutdown — leaving it (cannot prove ownership)', {\n pidFilePath,\n error: error instanceof Error ? error.message : String(error)\n });\n return;\n }\n\n if (recordedPid !== currentPid) {\n logger.debug('SYSTEM', 'PID file not owned by this process — leaving it for its owner (restart successor?)', {\n pidFilePath,\n recordedPid,\n currentPid\n });\n return;\n }\n\n try {\n rmSync(pidFilePath, { force: true });\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.debug('SYSTEM', 'Failed to remove PID file during shutdown', { pidFilePath }, error);\n } else {\n logger.warn('SYSTEM', 'Failed to remove PID file during shutdown (non-Error)', {\n pidFilePath,\n error: String(error)\n });\n }\n }\n}\n\nasync function signalProcess(record: ManagedProcessRecord, signal: 'SIGTERM' | 'SIGKILL'): Promise<void> {\n const { pid, pgid } = record;\n\n if (process.platform !== 'win32') {\n // Try the process group first when we have one — it reaches grandchildren\n // re-parented to init. If the group is already gone (ESRCH) the actual\n // root pid may still be alive (e.g. it survived its own group teardown);\n // fall through to the per-pid signal so shutdown isn't a no-op\n // (CodeRabbit review on PR #2282).\n if (typeof pgid === 'number') {\n try {\n process.kill(-pgid, signal);\n return;\n } catch (error: unknown) {\n const errno = error instanceof Error ? (error as NodeJS.ErrnoException).code : undefined;\n if (errno !== 'ESRCH') {\n throw error;\n }\n // ESRCH on the group — fall through and try the bare pid below.\n }\n }\n\n try {\n process.kill(pid, signal);\n } catch (error: unknown) {\n const errno = error instanceof Error ? (error as NodeJS.ErrnoException).code : undefined;\n if (errno !== 'ESRCH') {\n throw error;\n }\n }\n return;\n }\n\n if (signal === 'SIGTERM') {\n try {\n process.kill(pid, signal);\n } catch (error: unknown) {\n if (error instanceof Error) {\n const errno = (error as NodeJS.ErrnoException).code;\n if (errno === 'ESRCH') {\n return;\n }\n }\n throw error;\n }\n return;\n }\n\n const treeKill = await loadTreeKill();\n if (treeKill) {\n await new Promise<void>((resolve, reject) => {\n treeKill(pid, signal, (error) => {\n if (!error) {\n resolve();\n return;\n }\n\n const errno = (error as NodeJS.ErrnoException).code;\n if (errno === 'ESRCH') {\n resolve();\n return;\n }\n reject(error);\n });\n });\n return;\n }\n\n const args = ['/PID', String(pid), '/T'];\n if (signal === 'SIGKILL') {\n args.push('/F');\n }\n\n await execFileAsync('taskkill', args, {\n timeout: HOOK_TIMEOUTS.POWERSHELL_COMMAND,\n windowsHide: true\n });\n}\n\nasync function loadTreeKill(): Promise<TreeKillFn | null> {\n const moduleName = 'tree-kill';\n\n try {\n const treeKillModule = await import(moduleName);\n return (treeKillModule.default ?? treeKillModule) as TreeKillFn;\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'tree-kill module not available, using fallback', {}, error instanceof Error ? error : undefined);\n return null;\n }\n}\n","\nimport { logger } from '../utils/logger.js';\nimport { getProcessRegistry } from './process-registry.js';\n\nconst HEALTH_CHECK_INTERVAL_MS = 30_000;\n\nlet healthCheckInterval: ReturnType<typeof setInterval> | null = null;\n\nfunction runHealthCheck(): void {\n const registry = getProcessRegistry();\n\n const removedProcessCount = registry.pruneDeadEntries();\n if (removedProcessCount > 0) {\n logger.info('SYSTEM', `Health check: pruned ${removedProcessCount} dead process(es) from registry`);\n }\n}\n\nexport function startHealthChecker(): void {\n if (healthCheckInterval !== null) return;\n\n healthCheckInterval = setInterval(runHealthCheck, HEALTH_CHECK_INTERVAL_MS);\n healthCheckInterval.unref();\n\n logger.debug('SYSTEM', 'Health checker started', { intervalMs: HEALTH_CHECK_INTERVAL_MS });\n}\n\nexport function stopHealthChecker(): void {\n if (healthCheckInterval === null) return;\n\n clearInterval(healthCheckInterval);\n healthCheckInterval = null;\n\n logger.debug('SYSTEM', 'Health checker stopped');\n}\n","import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { SettingsDefaultsManager } from '../../shared/SettingsDefaultsManager.js';\nimport { logger } from '../../utils/logger.js';\n\ntype DocKind = 'observations' | 'summaries' | 'prompts';\n\nexport interface ProjectWatermarks {\n observations: number;\n summaries: number;\n prompts: number;\n}\n\nconst ZERO: ProjectWatermarks = { observations: 0, summaries: 0, prompts: 0 };\n\nfunction statePath(): string {\n const dataDir = SettingsDefaultsManager.get('CLAUDE_MEM_DATA_DIR');\n return join(dataDir, 'chroma-sync-state.json');\n}\n\nlet cache: Record<string, ProjectWatermarks> | null = null;\n\nfunction load(): Record<string, ProjectWatermarks> {\n if (cache) return cache;\n const path = statePath();\n if (!existsSync(path)) {\n cache = {};\n return cache;\n }\n const raw = readFileSync(path, 'utf8');\n const parsed = JSON.parse(raw) as Record<string, Partial<ProjectWatermarks>>;\n const normalized: Record<string, ProjectWatermarks> = {};\n for (const [project, marks] of Object.entries(parsed)) {\n normalized[project] = {\n observations: Number.isInteger(marks.observations) ? marks.observations as number : 0,\n summaries: Number.isInteger(marks.summaries) ? marks.summaries as number : 0,\n prompts: Number.isInteger(marks.prompts) ? marks.prompts as number : 0\n };\n }\n cache = normalized;\n return cache;\n}\n\nfunction persist(): void {\n if (!cache) return;\n const path = statePath();\n const dataDir = SettingsDefaultsManager.get('CLAUDE_MEM_DATA_DIR');\n if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, JSON.stringify(cache, null, 2), 'utf8');\n renameSync(tmp, path);\n}\n\nexport const ChromaSyncState = {\n exists(): boolean {\n return existsSync(statePath());\n },\n\n get(project: string): ProjectWatermarks {\n const all = load();\n return { ...(all[project] ?? ZERO) };\n },\n\n bump(project: string, kind: DocKind, id: number): void {\n if (!Number.isInteger(id) || id <= 0) return;\n const all = load();\n const current = all[project] ?? { ...ZERO };\n if (id <= current[kind]) return;\n current[kind] = id;\n all[project] = current;\n persist();\n },\n\n replace(project: string, marks: ProjectWatermarks): void {\n const all = load();\n all[project] = { ...marks };\n persist();\n }\n};\n","\nimport { ChromaMcpManager } from './ChromaMcpManager.js';\nimport { ChromaSyncState, ProjectWatermarks } from './ChromaSyncState.js';\nimport { ParsedObservation, ParsedSummary } from '../../sdk/parser.js';\n// cmem-sdk: keep SessionStore + parseFileList off the SDK's import graph.\n// Both come from the SQLite layer (`bun:sqlite`). The SDK only uses the\n// constructor + ensureCollectionExists + close() surface of ChromaSync,\n// so a TYPE-ONLY import is sufficient — value-level uses (`new\n// SessionStore()` / parseFileList(...)) are loaded lazily inside the\n// SQLite-only methods that need them. Plan §3 anti-pattern: do NOT add\n// `bun:sqlite` to the SDK bundle externals — fix the import chain.\nimport type { SessionStore as SessionStoreType } from '../sqlite/SessionStore.js';\nimport { logger } from '../../utils/logger.js';\nimport type * as SqliteFilesModule from '../sqlite/observations/files.js';\n\ntype SessionStore = SessionStoreType;\ntype SessionStoreCtor = new () => SessionStoreType;\n\n// Lazy CJS require so tsup (used by the cmem-sdk build) does not follow\n// these SQLite-coupled modules into the SDK bundle. Worker/Bun runtime\n// reaches them at first call; the SDK never calls the methods that\n// trigger these loads, so they never load in SDK consumers.\nconst lazyCreateRequire = (): ((id: string) => unknown) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mod = require('module') as typeof import('module');\n return mod.createRequire(import.meta.url);\n};\n\nlet _sessionStoreCtor: SessionStoreCtor | undefined;\nfunction loadSessionStoreCtor(): SessionStoreCtor {\n if (!_sessionStoreCtor) {\n const req = lazyCreateRequire();\n const m = req('../sqlite/SessionStore.js') as { SessionStore: SessionStoreCtor };\n _sessionStoreCtor = m.SessionStore;\n }\n return _sessionStoreCtor;\n}\n\nlet _filesHelper: typeof SqliteFilesModule | undefined;\nfunction loadFilesHelper(): typeof SqliteFilesModule {\n if (!_filesHelper) {\n const req = lazyCreateRequire();\n _filesHelper = req('../sqlite/observations/files.js') as typeof SqliteFilesModule;\n }\n return _filesHelper;\n}\n\n// Exported for cmem-sdk Phase 6: the SDK builds ChromaDocument values from\n// Postgres observations (UUID id, content string, metadata bag) and calls\n// the now-public addDocuments() to index them. Shape is unchanged.\nexport interface ChromaDocument {\n id: string;\n document: string;\n metadata: Record<string, string | number>;\n}\n\ninterface StoredObservation {\n id: number;\n memory_session_id: string;\n project: string;\n merged_into_project: string | null;\n text: string | null;\n type: string;\n title: string | null;\n subtitle: string | null;\n facts: string | null; \n narrative: string | null;\n concepts: string | null; \n files_read: string | null;\n files_modified: string | null;\n prompt_number: number;\n created_at_epoch: number;\n}\n\ninterface StoredSummary {\n id: number;\n memory_session_id: string;\n project: string;\n merged_into_project: string | null;\n request: string | null;\n investigated: string | null;\n learned: string | null;\n completed: string | null;\n next_steps: string | null;\n notes: string | null;\n prompt_number: number;\n created_at_epoch: number;\n}\n\ninterface StoredUserPrompt {\n id: number;\n content_session_id: string;\n prompt_number: number;\n prompt_text: string;\n created_at_epoch: number;\n memory_session_id: string;\n project: string;\n}\n\nexport class ChromaSync {\n private project: string;\n private collectionName: string;\n private collectionCreated = false;\n private readonly BATCH_SIZE = 100;\n\n constructor(project: string) {\n this.project = project;\n const sanitized = project\n .replace(/[^a-zA-Z0-9._-]/g, '_')\n .replace(/[^a-zA-Z0-9]+$/, ''); \n this.collectionName = `cm__${sanitized || 'unknown'}`;\n }\n\n /** Public: cmem-sdk reuses the per-tenant collection name for raw queries. */\n public getCollectionName(): string {\n return this.collectionName;\n }\n\n // Public: cmem-sdk requires Chroma at construction. Plan §3 line 192.\n public async ensureCollectionExists(): Promise<void> {\n if (this.collectionCreated) {\n return;\n }\n\n const chromaMcp = ChromaMcpManager.getInstance();\n try {\n await chromaMcp.callTool('chroma_create_collection', {\n collection_name: this.collectionName\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (!message.includes('already exists')) {\n throw error;\n }\n // Collection already exists - this is the expected path after first creation\n }\n\n this.collectionCreated = true;\n\n logger.debug('CHROMA_SYNC', 'Collection ready', {\n collection: this.collectionName\n });\n }\n\n private formatObservationDocs(obs: StoredObservation): ChromaDocument[] {\n const documents: ChromaDocument[] = [];\n\n const facts = obs.facts ? JSON.parse(obs.facts) : [];\n const concepts = obs.concepts ? JSON.parse(obs.concepts) : [];\n // parseFileList is SQLite-shaped (`bun:sqlite` in the import chain) —\n // resolve it through the deferred loader so this method stays out of\n // the SDK bundle's import graph. Plan §3.\n const filesHelper = loadFilesHelper();\n const files_read = filesHelper.parseFileList(obs.files_read);\n const files_modified = filesHelper.parseFileList(obs.files_modified);\n\n const baseMetadata: Record<string, string | number | null> = {\n sqlite_id: obs.id,\n doc_type: 'observation',\n memory_session_id: obs.memory_session_id,\n project: obs.project,\n merged_into_project: obs.merged_into_project ?? null,\n created_at_epoch: obs.created_at_epoch,\n type: obs.type || 'discovery',\n title: obs.title || 'Untitled'\n };\n\n if (obs.subtitle) {\n baseMetadata.subtitle = obs.subtitle;\n }\n if (concepts.length > 0) {\n baseMetadata.concepts = concepts.join(',');\n }\n if (files_read.length > 0) {\n baseMetadata.files_read = files_read.join(',');\n }\n if (files_modified.length > 0) {\n baseMetadata.files_modified = files_modified.join(',');\n }\n\n if (obs.narrative) {\n documents.push({\n id: `obs_${obs.id}_narrative`,\n document: obs.narrative,\n metadata: { ...baseMetadata, field_type: 'narrative' }\n });\n }\n\n if (obs.text) {\n documents.push({\n id: `obs_${obs.id}_text`,\n document: obs.text,\n metadata: { ...baseMetadata, field_type: 'text' }\n });\n }\n\n facts.forEach((fact: string, index: number) => {\n documents.push({\n id: `obs_${obs.id}_fact_${index}`,\n document: fact,\n metadata: { ...baseMetadata, field_type: 'fact', fact_index: index }\n });\n });\n\n return documents;\n }\n\n private formatSummaryDocs(summary: StoredSummary): ChromaDocument[] {\n const documents: ChromaDocument[] = [];\n\n const baseMetadata: Record<string, string | number | null> = {\n sqlite_id: summary.id,\n doc_type: 'session_summary',\n memory_session_id: summary.memory_session_id,\n project: summary.project,\n merged_into_project: summary.merged_into_project ?? null,\n created_at_epoch: summary.created_at_epoch,\n prompt_number: summary.prompt_number || 0\n };\n\n if (summary.request) {\n documents.push({\n id: `summary_${summary.id}_request`,\n document: summary.request,\n metadata: { ...baseMetadata, field_type: 'request' }\n });\n }\n\n if (summary.investigated) {\n documents.push({\n id: `summary_${summary.id}_investigated`,\n document: summary.investigated,\n metadata: { ...baseMetadata, field_type: 'investigated' }\n });\n }\n\n if (summary.learned) {\n documents.push({\n id: `summary_${summary.id}_learned`,\n document: summary.learned,\n metadata: { ...baseMetadata, field_type: 'learned' }\n });\n }\n\n if (summary.completed) {\n documents.push({\n id: `summary_${summary.id}_completed`,\n document: summary.completed,\n metadata: { ...baseMetadata, field_type: 'completed' }\n });\n }\n\n if (summary.next_steps) {\n documents.push({\n id: `summary_${summary.id}_next_steps`,\n document: summary.next_steps,\n metadata: { ...baseMetadata, field_type: 'next_steps' }\n });\n }\n\n if (summary.notes) {\n documents.push({\n id: `summary_${summary.id}_notes`,\n document: summary.notes,\n metadata: { ...baseMetadata, field_type: 'notes' }\n });\n }\n\n return documents;\n }\n\n /**\n * Write `documents` to Chroma in BATCH_SIZE-sized batches.\n *\n * Returns the number of documents that were successfully written (or\n * confirmed via delete+add reconcile). Per-batch failures are logged and the\n * loop continues — we never throw — so callers must use the returned count\n * to advance their watermark, otherwise an interrupted backfill can mark\n * unsynced records as synced.\n *\n * Visibility: promoted from `private` to `public` for cmem-sdk Phase 6.\n * The SDK indexes Postgres observations into Chroma using this same\n * storage-agnostic document layer — same retry/dedupe semantics, same\n * BATCH_SIZE. SQLite-shaped `syncObservation` is NOT reusable for the\n * Postgres UUID path. See plan §6 line 244-247.\n */\n public async addDocuments(documents: ChromaDocument[]): Promise<number> {\n if (documents.length === 0) {\n return 0;\n }\n\n await this.ensureCollectionExists();\n\n const chromaMcp = ChromaMcpManager.getInstance();\n\n let written = 0;\n for (let i = 0; i < documents.length; i += this.BATCH_SIZE) {\n const batch = documents.slice(i, i + this.BATCH_SIZE);\n\n const cleanMetadatas = batch.map(d =>\n Object.fromEntries(\n Object.entries(d.metadata).filter(([_, v]) => v !== null && v !== undefined && v !== '')\n )\n );\n\n try {\n await chromaMcp.callTool('chroma_add_documents', {\n collection_name: this.collectionName,\n ids: batch.map(d => d.id),\n documents: batch.map(d => d.document),\n metadatas: cleanMetadatas\n });\n written += batch.length;\n } catch (error) {\n const errMsg = error instanceof Error ? error.message : String(error);\n if (errMsg.includes('already exist')) {\n try {\n await chromaMcp.callTool('chroma_delete_documents', {\n collection_name: this.collectionName,\n ids: batch.map(d => d.id)\n });\n await chromaMcp.callTool('chroma_add_documents', {\n collection_name: this.collectionName,\n ids: batch.map(d => d.id),\n documents: batch.map(d => d.document),\n metadatas: cleanMetadatas\n });\n written += batch.length;\n logger.info('CHROMA_SYNC', 'Batch reconciled via delete+add after duplicate conflict', {\n collection: this.collectionName,\n batchStart: i,\n batchSize: batch.length\n });\n } catch (reconcileError) {\n logger.error('CHROMA_SYNC', 'Batch reconcile (delete+add) failed — watermark will not advance for this batch', {\n collection: this.collectionName,\n batchStart: i,\n batchSize: batch.length\n }, reconcileError as Error);\n }\n } else {\n logger.error('CHROMA_SYNC', 'Batch add failed — watermark will not advance for this batch, continuing with remaining batches', {\n collection: this.collectionName,\n batchStart: i,\n batchSize: batch.length\n }, error as Error);\n }\n }\n }\n\n logger.debug('CHROMA_SYNC', 'Documents added', {\n collection: this.collectionName,\n requested: documents.length,\n written\n });\n return written;\n }\n\n async syncObservation(\n observationId: number,\n memorySessionId: string,\n project: string,\n obs: ParsedObservation,\n promptNumber: number,\n createdAtEpoch: number\n ): Promise<void> {\n const stored: StoredObservation = {\n id: observationId,\n memory_session_id: memorySessionId,\n project: project,\n merged_into_project: null,\n text: null, // Legacy field, not used\n type: obs.type,\n title: obs.title,\n subtitle: obs.subtitle,\n facts: JSON.stringify(obs.facts),\n narrative: obs.narrative,\n concepts: JSON.stringify(obs.concepts),\n files_read: JSON.stringify(obs.files_read),\n files_modified: JSON.stringify(obs.files_modified),\n prompt_number: promptNumber,\n created_at_epoch: createdAtEpoch\n };\n\n const documents = this.formatObservationDocs(stored);\n\n logger.info('CHROMA_SYNC', 'Syncing observation', {\n observationId,\n documentCount: documents.length,\n project\n });\n\n // Only advance the watermark on a confirmed full write. addDocuments() now\n // returns a written count and tolerates per-batch failures, so a transient\n // Chroma error must NOT mark this observation as synced — otherwise the\n // backfill pass on next boot will skip past it (CodeRabbit review on PR\n // #2282).\n const written = await this.addDocuments(documents);\n if (written === documents.length) {\n ChromaSyncState.bump(project, 'observations', observationId);\n } else {\n logger.warn('CHROMA_SYNC', 'Observation watermark bump skipped — partial write', {\n observationId,\n project,\n requested: documents.length,\n written\n });\n }\n }\n\n async syncSummary(\n summaryId: number,\n memorySessionId: string,\n project: string,\n summary: ParsedSummary,\n promptNumber: number,\n createdAtEpoch: number\n ): Promise<void> {\n const stored: StoredSummary = {\n id: summaryId,\n memory_session_id: memorySessionId,\n project: project,\n merged_into_project: null,\n request: summary.request,\n investigated: summary.investigated,\n learned: summary.learned,\n completed: summary.completed,\n next_steps: summary.next_steps,\n notes: summary.notes,\n prompt_number: promptNumber,\n created_at_epoch: createdAtEpoch\n };\n\n const documents = this.formatSummaryDocs(stored);\n\n logger.info('CHROMA_SYNC', 'Syncing summary', {\n summaryId,\n documentCount: documents.length,\n project\n });\n\n // Only bump on a confirmed full write — see syncObservation() for rationale.\n const written = await this.addDocuments(documents);\n if (written === documents.length) {\n ChromaSyncState.bump(project, 'summaries', summaryId);\n } else {\n logger.warn('CHROMA_SYNC', 'Summary watermark bump skipped — partial write', {\n summaryId,\n project,\n requested: documents.length,\n written\n });\n }\n }\n\n private formatUserPromptDoc(prompt: StoredUserPrompt): ChromaDocument {\n return {\n id: `prompt_${prompt.id}`,\n document: prompt.prompt_text,\n metadata: {\n sqlite_id: prompt.id,\n doc_type: 'user_prompt',\n memory_session_id: prompt.memory_session_id,\n project: prompt.project,\n created_at_epoch: prompt.created_at_epoch,\n prompt_number: prompt.prompt_number\n }\n };\n }\n\n async syncUserPrompt(\n promptId: number,\n memorySessionId: string,\n project: string,\n promptText: string,\n promptNumber: number,\n createdAtEpoch: number\n ): Promise<void> {\n const stored: StoredUserPrompt = {\n id: promptId,\n content_session_id: '', // Not needed for Chroma sync\n prompt_number: promptNumber,\n prompt_text: promptText,\n created_at_epoch: createdAtEpoch,\n memory_session_id: memorySessionId,\n project: project\n };\n\n const document = this.formatUserPromptDoc(stored);\n\n logger.info('CHROMA_SYNC', 'Syncing user prompt', {\n promptId,\n project\n });\n\n // Only bump on a confirmed full write — see syncObservation() for rationale.\n const written = await this.addDocuments([document]);\n if (written === 1) {\n ChromaSyncState.bump(project, 'prompts', promptId);\n } else {\n logger.warn('CHROMA_SYNC', 'Prompt watermark bump skipped — write failed', {\n promptId,\n project,\n written\n });\n }\n }\n\n private async getExistingChromaIds(projectOverride?: string): Promise<{\n observations: Set<number>;\n summaries: Set<number>;\n prompts: Set<number>;\n }> {\n const targetProject = projectOverride ?? this.project;\n await this.ensureCollectionExists();\n\n const chromaMcp = ChromaMcpManager.getInstance();\n\n const observationIds = new Set<number>();\n const summaryIds = new Set<number>();\n const promptIds = new Set<number>();\n\n let offset = 0;\n const limit = 1000; \n\n logger.info('CHROMA_SYNC', 'Fetching existing Chroma document IDs...', { project: targetProject });\n\n while (true) {\n const result = await chromaMcp.callTool('chroma_get_documents', {\n collection_name: this.collectionName,\n limit: limit,\n offset: offset,\n where: { project: targetProject },\n include: ['metadatas']\n }) as any;\n\n const metadatas = result?.metadatas || [];\n\n if (metadatas.length === 0) {\n break; \n }\n\n for (const meta of metadatas) {\n if (meta && meta.sqlite_id) {\n const sqliteId = meta.sqlite_id as number;\n if (meta.doc_type === 'observation') {\n observationIds.add(sqliteId);\n } else if (meta.doc_type === 'session_summary') {\n summaryIds.add(sqliteId);\n } else if (meta.doc_type === 'user_prompt') {\n promptIds.add(sqliteId);\n }\n }\n }\n\n offset += limit;\n\n logger.debug('CHROMA_SYNC', 'Fetched batch of existing IDs', {\n project: targetProject,\n offset,\n batchSize: metadatas.length\n });\n }\n\n logger.info('CHROMA_SYNC', 'Existing IDs fetched', {\n project: targetProject,\n observations: observationIds.size,\n summaries: summaryIds.size,\n prompts: promptIds.size,\n total: observationIds.size + summaryIds.size + promptIds.size\n });\n\n return { observations: observationIds, summaries: summaryIds, prompts: promptIds };\n }\n\n async bootstrapWatermarksFromChroma(project: string): Promise<void> {\n const existing = await this.getExistingChromaIds(project);\n const max = (set: Set<number>): number => (set.size ? Math.max(...set) : 0);\n ChromaSyncState.replace(project, {\n observations: max(existing.observations),\n summaries: max(existing.summaries),\n prompts: max(existing.prompts)\n });\n logger.info('CHROMA_SYNC', 'Bootstrapped watermarks from Chroma', {\n project,\n watermarks: ChromaSyncState.get(project)\n });\n }\n\n async ensureBackfilled(projectOverride?: string, storeOverride?: SessionStore): Promise<void> {\n const backfillProject = projectOverride ?? this.project;\n logger.info('CHROMA_SYNC', 'Starting smart backfill', { project: backfillProject });\n\n await this.ensureCollectionExists();\n\n const watermarks = ChromaSyncState.get(backfillProject);\n\n const SessionStoreCtor = loadSessionStoreCtor();\n const db = storeOverride ?? new SessionStoreCtor();\n\n try {\n await this.runBackfillPipeline(db, backfillProject, watermarks);\n } catch (error) {\n logger.error('CHROMA_SYNC', 'Backfill failed', { project: backfillProject }, error instanceof Error ? error : new Error(String(error)));\n throw new Error(`Backfill failed: ${error instanceof Error ? error.message : String(error)}`);\n } finally {\n if (!storeOverride) {\n db.close();\n }\n }\n }\n\n private async runBackfillPipeline(\n db: SessionStore,\n backfillProject: string,\n watermarks: ProjectWatermarks\n ): Promise<void> {\n const allDocs = await this.backfillObservations(db, backfillProject, watermarks.observations);\n const summaryDocs = await this.backfillSummaries(db, backfillProject, watermarks.summaries);\n const promptDocs = await this.backfillPrompts(db, backfillProject, watermarks.prompts);\n\n logger.info('CHROMA_SYNC', 'Smart backfill complete', {\n project: backfillProject,\n synced: {\n observationDocs: allDocs.length,\n summaryDocs: summaryDocs.length,\n promptDocs: promptDocs.length\n },\n watermarks: ChromaSyncState.get(backfillProject)\n });\n }\n\n private async backfillObservations(\n db: SessionStore,\n backfillProject: string,\n watermark: number\n ): Promise<ChromaDocument[]> {\n const observations = db.db.prepare(`\n SELECT * FROM observations\n WHERE project = ? AND id > ?\n ORDER BY id ASC\n `).all(backfillProject, watermark) as StoredObservation[];\n\n if (observations.length === 0) {\n return [];\n }\n\n const totalObsCount = db.db.prepare(`\n SELECT COUNT(*) as count FROM observations WHERE project = ?\n `).get(backfillProject) as { count: number };\n\n logger.info('CHROMA_SYNC', 'Backfilling observations', {\n project: backfillProject,\n missing: observations.length,\n watermark,\n total: totalObsCount.count\n });\n\n const allDocs: ChromaDocument[] = [];\n const obsByDocCount: Array<{ obs: StoredObservation; docs: ChromaDocument[] }> = [];\n for (const obs of observations) {\n const docs = this.formatObservationDocs(obs);\n allDocs.push(...docs);\n obsByDocCount.push({ obs, docs });\n }\n\n // Watermark must be durable per-batch: SIGKILL / OOM / reboot mid-flight\n // skips any trailing finally, so a once-at-end bump leaves the watermark\n // at zero and the next boot re-embeds everything (#2214, amplifies #2220).\n //\n // Non-contiguous failure guard: once any batch under-writes, ALL later\n // batches must also skip the watermark bump. The watermark is a single\n // monotonic id, so it cannot represent \"synced through 200, then a gap at\n // 201–250, then 251 onward\" — bumping past the gap would silently drop\n // 201–250 forever (CodeRabbit review on PR #2282).\n let writtenDocs = 0;\n let lastSyncedObsIdx = -1;\n let hadGap = false;\n for (let i = 0; i < allDocs.length; i += this.BATCH_SIZE) {\n const batch = allDocs.slice(i, i + this.BATCH_SIZE);\n const writtenInBatch = await this.addDocuments(batch);\n // Only advance the watermark for documents that actually landed in\n // Chroma. addDocuments() logs and continues on per-batch failures, so a\n // partial write must not mark unwritten docs as synced.\n if (writtenInBatch < batch.length) {\n hadGap = true;\n logger.debug('CHROMA_SYNC', 'Skipping watermark bump for failed/partial batch', {\n project: backfillProject,\n batchStart: i,\n requested: batch.length,\n written: writtenInBatch\n });\n continue;\n }\n if (hadGap) {\n // A previous batch left a gap; downstream batches cannot bump the\n // watermark even if they themselves succeeded.\n logger.debug('CHROMA_SYNC', 'Skipping watermark bump after prior gap', {\n project: backfillProject,\n batchStart: i\n });\n continue;\n }\n writtenDocs += writtenInBatch;\n\n let cursor = 0;\n for (let j = 0; j < obsByDocCount.length; j++) {\n cursor += obsByDocCount[j].docs.length;\n if (cursor <= writtenDocs) {\n lastSyncedObsIdx = j;\n } else {\n break;\n }\n }\n\n if (lastSyncedObsIdx >= 0) {\n ChromaSyncState.bump(\n backfillProject,\n 'observations',\n obsByDocCount[lastSyncedObsIdx].obs.id\n );\n }\n\n logger.debug('CHROMA_SYNC', 'Backfill progress', {\n project: backfillProject,\n progress: `${Math.min(i + this.BATCH_SIZE, allDocs.length)}/${allDocs.length}`\n });\n }\n\n return allDocs;\n }\n\n private async backfillSummaries(\n db: SessionStore,\n backfillProject: string,\n watermark: number\n ): Promise<ChromaDocument[]> {\n const summaries = db.db.prepare(`\n SELECT * FROM session_summaries\n WHERE project = ? AND id > ?\n ORDER BY id ASC\n `).all(backfillProject, watermark) as StoredSummary[];\n\n if (summaries.length === 0) {\n return [];\n }\n\n const totalSummaryCount = db.db.prepare(`\n SELECT COUNT(*) as count FROM session_summaries WHERE project = ?\n `).get(backfillProject) as { count: number };\n\n logger.info('CHROMA_SYNC', 'Backfilling summaries', {\n project: backfillProject,\n missing: summaries.length,\n watermark,\n total: totalSummaryCount.count\n });\n\n const summaryDocs: ChromaDocument[] = [];\n const summaryByDocCount: Array<{ summary: StoredSummary; docs: ChromaDocument[] }> = [];\n for (const summary of summaries) {\n const docs = this.formatSummaryDocs(summary);\n summaryDocs.push(...docs);\n summaryByDocCount.push({ summary, docs });\n }\n\n // Non-contiguous failure guard: see backfillObservations() for rationale.\n let writtenDocs = 0;\n let lastSyncedIdx = -1;\n let hadGap = false;\n for (let i = 0; i < summaryDocs.length; i += this.BATCH_SIZE) {\n const batch = summaryDocs.slice(i, i + this.BATCH_SIZE);\n const writtenInBatch = await this.addDocuments(batch);\n // Only advance the watermark for documents that actually landed in\n // Chroma. See the analogous comment in backfillObservations().\n if (writtenInBatch < batch.length) {\n hadGap = true;\n logger.debug('CHROMA_SYNC', 'Skipping watermark bump for failed/partial batch', {\n project: backfillProject,\n batchStart: i,\n requested: batch.length,\n written: writtenInBatch\n });\n continue;\n }\n if (hadGap) {\n logger.debug('CHROMA_SYNC', 'Skipping watermark bump after prior gap', {\n project: backfillProject,\n batchStart: i\n });\n continue;\n }\n writtenDocs += writtenInBatch;\n\n let cursor = 0;\n for (let j = 0; j < summaryByDocCount.length; j++) {\n cursor += summaryByDocCount[j].docs.length;\n if (cursor <= writtenDocs) lastSyncedIdx = j;\n else break;\n }\n\n if (lastSyncedIdx >= 0) {\n ChromaSyncState.bump(\n backfillProject,\n 'summaries',\n summaryByDocCount[lastSyncedIdx].summary.id\n );\n }\n\n logger.debug('CHROMA_SYNC', 'Backfill progress', {\n project: backfillProject,\n progress: `${Math.min(i + this.BATCH_SIZE, summaryDocs.length)}/${summaryDocs.length}`\n });\n }\n\n return summaryDocs;\n }\n\n private async backfillPrompts(\n db: SessionStore,\n backfillProject: string,\n watermark: number\n ): Promise<ChromaDocument[]> {\n const prompts = db.db.prepare(`\n SELECT\n up.*,\n s.project,\n s.memory_session_id\n FROM user_prompts up\n JOIN sdk_sessions s ON up.content_session_id = s.content_session_id\n WHERE s.project = ? AND up.id > ?\n ORDER BY up.id ASC\n `).all(backfillProject, watermark) as StoredUserPrompt[];\n\n if (prompts.length === 0) {\n return [];\n }\n\n const totalPromptCount = db.db.prepare(`\n SELECT COUNT(*) as count\n FROM user_prompts up\n JOIN sdk_sessions s ON up.content_session_id = s.content_session_id\n WHERE s.project = ?\n `).get(backfillProject) as { count: number };\n\n logger.info('CHROMA_SYNC', 'Backfilling user prompts', {\n project: backfillProject,\n missing: prompts.length,\n watermark,\n total: totalPromptCount.count\n });\n\n const promptDocs: ChromaDocument[] = [];\n for (const prompt of prompts) {\n promptDocs.push(this.formatUserPromptDoc(prompt));\n }\n\n // Prompts are 1 doc each — bump the watermark per batch so an interrupted\n // backfill resumes where it left off instead of re-embedding from zero.\n // Only advance the watermark when the batch actually wrote — partial\n // writes must not skip the failed prompts on restart.\n //\n // Non-contiguous failure guard: see backfillObservations() for rationale.\n let hadGap = false;\n for (let i = 0; i < promptDocs.length; i += this.BATCH_SIZE) {\n const batch = promptDocs.slice(i, i + this.BATCH_SIZE);\n const writtenInBatch = await this.addDocuments(batch);\n const upTo = Math.min(i + this.BATCH_SIZE, prompts.length);\n if (writtenInBatch < batch.length) {\n hadGap = true;\n logger.debug('CHROMA_SYNC', 'Skipping prompt watermark bump for failed/partial batch', {\n project: backfillProject,\n batchStart: i,\n requested: batch.length,\n written: writtenInBatch\n });\n continue;\n }\n if (hadGap) {\n logger.debug('CHROMA_SYNC', 'Skipping prompt watermark bump after prior gap', {\n project: backfillProject,\n batchStart: i\n });\n continue;\n }\n const lastSyncedPromptId = prompts[upTo - 1].id;\n ChromaSyncState.bump(backfillProject, 'prompts', lastSyncedPromptId);\n\n logger.debug('CHROMA_SYNC', 'Backfill progress', {\n project: backfillProject,\n progress: `${upTo}/${promptDocs.length}`\n });\n }\n\n return promptDocs;\n }\n\n async queryChroma(\n query: string,\n limit: number,\n whereFilter?: Record<string, any>\n ): Promise<{ ids: number[]; distances: number[]; metadatas: any[] }> {\n await this.ensureCollectionExists();\n\n let results: any;\n try {\n const chromaMcp = ChromaMcpManager.getInstance();\n results = await chromaMcp.callTool('chroma_query_documents', {\n collection_name: this.collectionName,\n query_texts: [query],\n n_results: limit,\n ...(whereFilter && { where: whereFilter }),\n include: ['documents', 'metadatas', 'distances']\n });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n const isConnectionError =\n errorMessage.includes('ECONNREFUSED') || \n errorMessage.includes('ENOTFOUND') || \n errorMessage.includes('fetch failed') || \n errorMessage.includes('subprocess closed') || \n errorMessage.includes('timed out'); \n\n if (isConnectionError) {\n this.collectionCreated = false;\n logger.error('CHROMA_SYNC', 'Connection lost during query',\n { project: this.project, query }, error as Error);\n throw new Error(`Chroma query failed - connection lost: ${errorMessage}`);\n }\n\n logger.error('CHROMA_SYNC', 'Query failed', { project: this.project, query }, error as Error);\n throw error;\n }\n\n return this.deduplicateQueryResults(results);\n }\n\n private deduplicateQueryResults(results: any): { ids: number[]; distances: number[]; metadatas: any[] } {\n const ids: number[] = [];\n const seen = new Set<string>();\n const docIds = results?.ids?.[0] || [];\n const rawMetadatas = results?.metadatas?.[0] || [];\n const rawDistances = results?.distances?.[0] || [];\n\n const metadatas: any[] = [];\n const distances: number[] = [];\n\n for (let i = 0; i < docIds.length; i++) {\n const docId = docIds[i];\n const obsMatch = docId.match(/obs_(\\d+)_/);\n const summaryMatch = docId.match(/summary_(\\d+)_/);\n const promptMatch = docId.match(/prompt_(\\d+)/);\n\n let sqliteId: number | null = null;\n let entityType: string | null = null;\n if (obsMatch) {\n sqliteId = parseInt(obsMatch[1], 10);\n entityType = 'observation';\n } else if (summaryMatch) {\n sqliteId = parseInt(summaryMatch[1], 10);\n entityType = 'session_summary';\n } else if (promptMatch) {\n sqliteId = parseInt(promptMatch[1], 10);\n entityType = 'user_prompt';\n }\n\n if (sqliteId !== null && entityType) {\n const dedupeKey = `${entityType}:${sqliteId}`;\n if (seen.has(dedupeKey)) continue;\n seen.add(dedupeKey);\n ids.push(sqliteId);\n metadatas.push(rawMetadatas[i] ?? null);\n distances.push(rawDistances[i] ?? 0);\n }\n }\n\n return { ids, distances, metadatas };\n }\n\n /** Maximum number of concurrent project backfills to run at once. */\n private static readonly BACKFILL_CONCURRENCY_LIMIT = 3;\n\n /** Guard flag to prevent overlapping backfill runs from fire-and-forget callers. */\n private static backfillInProgress = false;\n\n /**\n * Backfill all projects that have observations in SQLite but may be missing from Chroma.\n * Uses a single shared ChromaSync('claude-mem') instance and Chroma connection.\n * Per-project scoping is passed as a parameter to ensureBackfilled(), avoiding\n * instance state mutation. All documents land in the cm__claude-mem collection\n * with project scoped via metadata, matching how DatabaseManager and SearchManager operate.\n * Designed to be called fire-and-forget on worker startup.\n *\n * Concurrency: processes at most BACKFILL_CONCURRENCY_LIMIT projects in parallel\n * to bound CPU and memory pressure from concurrent Chroma embedding operations.\n * A re-entrant guard prevents overlapping backfill runs from accumulating.\n */\n static async backfillAllProjects(storeOverride?: SessionStore): Promise<void> {\n if (ChromaSync.backfillInProgress) {\n logger.info('CHROMA_SYNC', 'Backfill already in progress, skipping duplicate run');\n return;\n }\n\n // Allocate first so a constructor throw cannot leave the guard stuck true\n // and silently skip every subsequent backfill (CodeRabbit review on PR\n // #2282). The guard only flips to true after both resources are alive,\n // and the finally always clears it.\n let db: SessionStore | undefined;\n let sync: ChromaSync | undefined;\n try {\n const SessionStoreCtor = loadSessionStoreCtor();\n db = storeOverride ?? new SessionStoreCtor();\n sync = new ChromaSync('claude-mem');\n } catch (error) {\n logger.error('CHROMA_SYNC', 'Failed to initialize backfill resources',\n {}, error instanceof Error ? error : new Error(String(error)));\n // Best-effort cleanup if SessionStore allocated but ChromaSync threw.\n if (db && !storeOverride) {\n try { db.close(); } catch { /* ignore */ }\n }\n throw error;\n }\n\n ChromaSync.backfillInProgress = true;\n try {\n const projects = db.db.prepare(\n 'SELECT DISTINCT project FROM observations WHERE project IS NOT NULL AND project != ?'\n ).all('') as { project: string }[];\n\n logger.info('CHROMA_SYNC', `Backfill check for ${projects.length} projects`);\n\n if (!ChromaSyncState.exists()) {\n logger.info('CHROMA_SYNC', 'Watermark cache missing — bootstrapping from Chroma (one-time)');\n for (const { project } of projects) {\n try {\n await sync.bootstrapWatermarksFromChroma(project);\n } catch (error) {\n logger.error('CHROMA_SYNC', `Bootstrap failed for project: ${project}`,\n {}, error instanceof Error ? error : new Error(String(error)));\n }\n }\n logger.info('CHROMA_SYNC', 'Bootstrap complete — incremental backfills will use watermarks');\n }\n\n // Process projects in chunks of BACKFILL_CONCURRENCY_LIMIT to bound\n // CPU/memory pressure from concurrent Chroma embedding operations.\n // Each chunk runs its projects in parallel; we wait for the entire chunk\n // before starting the next one. Simple and predictable — no semaphore\n // overhead, no unbounded fan-out.\n const concurrency = ChromaSync.BACKFILL_CONCURRENCY_LIMIT;\n for (let i = 0; i < projects.length; i += concurrency) {\n const chunk = projects.slice(i, i + concurrency);\n const chunkResults = await Promise.allSettled(\n chunk.map(({ project }) => sync!.ensureBackfilled(project, db!))\n );\n\n for (let j = 0; j < chunkResults.length; j++) {\n const result = chunkResults[j];\n if (result.status === 'rejected') {\n const project = chunk[j].project;\n const error = result.reason;\n if (error instanceof Error) {\n logger.error('CHROMA_SYNC', `Backfill failed for project: ${project}`, {}, error);\n } else {\n logger.error('CHROMA_SYNC', `Backfill failed for project: ${project}`, { error: String(error) });\n }\n // Continue to next chunk — don't let one failure stop others\n }\n }\n }\n } finally {\n ChromaSync.backfillInProgress = false;\n if (sync) {\n try { await sync.close(); } catch (closeError) {\n logger.debug('CHROMA_SYNC', 'sync.close() failed during backfill teardown',\n {}, closeError instanceof Error ? closeError : new Error(String(closeError)));\n }\n }\n if (!storeOverride && db) {\n try { db.close(); } catch (closeError) {\n logger.debug('CHROMA_SYNC', 'db.close() failed during backfill teardown',\n {}, closeError instanceof Error ? closeError : new Error(String(closeError)));\n }\n }\n }\n }\n\n async updateMergedIntoProject(\n sqliteIds: number[],\n mergedIntoProject: string\n ): Promise<void> {\n if (sqliteIds.length === 0) return;\n\n await this.ensureCollectionExists();\n const chromaMcp = ChromaMcpManager.getInstance();\n\n let totalPatched = 0;\n\n for (let i = 0; i < sqliteIds.length; i += this.BATCH_SIZE) {\n const idBatch = sqliteIds.slice(i, i + this.BATCH_SIZE);\n\n const existing = await chromaMcp.callTool('chroma_get_documents', {\n collection_name: this.collectionName,\n where: { sqlite_id: { $in: idBatch } },\n include: ['metadatas']\n }) as { ids?: string[]; metadatas?: Array<Record<string, any> | null> };\n\n const docIds: string[] = existing?.ids ?? [];\n if (docIds.length === 0) continue;\n\n const metadatas = (existing?.metadatas ?? []).map(m => {\n const merged: Record<string, any> = {\n ...(m ?? {}),\n merged_into_project: mergedIntoProject\n };\n return Object.fromEntries(\n Object.entries(merged).filter(\n ([, v]) => v !== null && v !== undefined && v !== ''\n )\n );\n });\n\n await chromaMcp.callTool('chroma_update_documents', {\n collection_name: this.collectionName,\n ids: docIds,\n metadatas\n });\n totalPatched += docIds.length;\n }\n\n logger.info('CHROMA_SYNC', 'merged_into_project metadata patched', {\n collection: this.collectionName,\n mergedIntoProject,\n sqliteIdCount: sqliteIds.length,\n chromaDocsPatched: totalPatched\n });\n }\n\n async close(): Promise<void> {\n logger.info('CHROMA_SYNC', 'ChromaSync closed', { project: this.project });\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { createHash } from 'crypto';\nimport { SERVER_JOB_KIND_PREFIX, type ServerGenerationJobKind } from './types.js';\n\nexport interface ServerJobIdParts {\n kind: ServerGenerationJobKind;\n team_id: string;\n project_id: string;\n source_type: string;\n source_id: string;\n}\n\n// SHA-256-derived deterministic IDs avoid Redis key collisions across tenants\n// and keep BullMQ jobId deduplication intact across process restarts.\n// Format: `${kindPrefix}_${sha256hex}` with NO ':' characters (BullMQ uses ':'\n// internally as a key separator; embedding ':' in jobIds causes scan/state\n// confusion).\nexport function buildServerJobId(parts: ServerJobIdParts): string {\n const prefix = SERVER_JOB_KIND_PREFIX[parts.kind];\n const canonical = JSON.stringify({\n kind: parts.kind,\n team_id: parts.team_id,\n project_id: parts.project_id,\n source_type: parts.source_type,\n source_id: parts.source_id\n });\n const digest = createHash('sha256').update(canonical).digest('hex');\n return `${prefix}_${digest}`;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { z } from 'zod';\nimport type {\n ObservationGenerationJobSourceType,\n ObservationGenerationJobStatus\n} from '../../storage/postgres/generation-jobs.js';\n\nexport type ServerGenerationJobKind = 'event' | 'event-batch' | 'summary' | 'reindex';\n\nexport type ServerGenerationJobStatus = ObservationGenerationJobStatus;\n\n// Phase 11 — every BullMQ job carries the full team-aware tracing surface so\n// the worker can audit and scope-check on every retry. team_id and project_id\n// are advisory: the worker MUST reload the canonical outbox row from Postgres\n// and compare these fields before any side effect. Treating these as auth\n// authority would be a bypass — the comparison is a tampering detector, not\n// the auth gate.\nexport interface ServerGenerationJob {\n kind: ServerGenerationJobKind;\n team_id: string;\n project_id: string;\n source_type: ObservationGenerationJobSourceType;\n source_id: string;\n generation_job_id: string;\n // Identity of the API key that initiated this job at the HTTP boundary.\n // Reused at execution time to detect revocation between enqueue and run.\n api_key_id: string | null;\n // The actor associated with the api key at enqueue time. Audit-only;\n // never trust this for authz decisions.\n actor_id: string | null;\n // Legacy adapter or surface that produced the source row, for routing\n // and audit (e.g. 'api', 'hooks', 'mcp', 'compat:sessions-observations').\n source_adapter: string;\n // Phase 12 — request correlation id, optional but always serialized as a\n // nullable field so downstream consumers can rely on shape stability.\n request_id?: string | null;\n}\n\nexport interface GenerateObservationsForEventJob extends ServerGenerationJob {\n kind: 'event';\n agent_event_id: string;\n}\n\nexport interface GenerateObservationsForEventBatchJob extends ServerGenerationJob {\n kind: 'event-batch';\n agent_event_ids: string[];\n}\n\nexport interface GenerateSessionSummaryJob extends ServerGenerationJob {\n kind: 'summary';\n server_session_id: string;\n}\n\nexport interface ReindexObservationJob extends ServerGenerationJob {\n kind: 'reindex';\n observation_id: string;\n}\n\nexport type ServerGenerationJobPayload =\n | GenerateObservationsForEventJob\n | GenerateObservationsForEventBatchJob\n | GenerateSessionSummaryJob\n | ReindexObservationJob;\n\nexport const SERVER_JOB_QUEUE_NAMES: Record<ServerGenerationJobKind, string> = {\n event: 'server_beta_generate_event',\n 'event-batch': 'server_beta_generate_event_batch',\n summary: 'server_beta_generate_summary',\n reindex: 'server_beta_reindex'\n};\n\nexport const SERVER_JOB_KIND_PREFIX: Record<ServerGenerationJobKind, string> = {\n event: 'evt',\n 'event-batch': 'evtb',\n summary: 'sum',\n reindex: 'rdx'\n};\n\n// Phase 11 — Zod schema validates payloads at the queue boundary so a\n// malformed enqueue is rejected synchronously rather than silently producing\n// a job the worker can't audit. Required fields here mirror the\n// ServerGenerationJob interface; a missing team_id, project_id, or\n// generation_job_id should always be a programmer error caught at enqueue.\n\nconst baseFieldsSchema = z.object({\n team_id: z.string().min(1, 'team_id is required'),\n project_id: z.string().min(1, 'project_id is required'),\n source_type: z.enum(['agent_event', 'session_summary', 'observation_reindex']),\n source_id: z.string().min(1, 'source_id is required'),\n generation_job_id: z.string().min(1, 'generation_job_id is required'),\n // api_key_id and actor_id are nullable to accommodate local-dev/system\n // enqueues, but the *field* must be present in the payload so audit\n // records always render the same shape.\n api_key_id: z.string().min(1).nullable(),\n actor_id: z.string().min(1).nullable(),\n source_adapter: z.string().min(1, 'source_adapter is required'),\n // Phase 12 — request_id is optional in the schema (older jobs predating\n // this phase have nullable/missing values) but always passes through to\n // logs and audit when present.\n request_id: z.string().min(1).nullable().optional(),\n});\n\nexport const GenerateObservationsForEventJobSchema = baseFieldsSchema.extend({\n kind: z.literal('event'),\n agent_event_id: z.string().min(1),\n});\n\nexport const GenerateObservationsForEventBatchJobSchema = baseFieldsSchema.extend({\n kind: z.literal('event-batch'),\n agent_event_ids: z.array(z.string().min(1)).min(1),\n});\n\nexport const GenerateSessionSummaryJobSchema = baseFieldsSchema.extend({\n kind: z.literal('summary'),\n server_session_id: z.string().min(1),\n});\n\nexport const ReindexObservationJobSchema = baseFieldsSchema.extend({\n kind: z.literal('reindex'),\n observation_id: z.string().min(1),\n});\n\nexport const ServerGenerationJobPayloadSchema = z.discriminatedUnion('kind', [\n GenerateObservationsForEventJobSchema,\n GenerateObservationsForEventBatchJobSchema,\n GenerateSessionSummaryJobSchema,\n ReindexObservationJobSchema,\n]);\n\nexport class ServerGenerationJobPayloadValidationError extends Error {\n readonly issues: z.ZodIssue[];\n\n constructor(issues: z.ZodIssue[]) {\n super(`invalid server generation job payload: ${issues.map(i => i.message).join('; ')}`);\n this.issues = issues;\n }\n}\n\n/**\n * Validate a candidate BullMQ payload against the discriminated union and\n * return a typed payload, or throw `ServerGenerationJobPayloadValidationError`.\n * Use this at every enqueue site so a malformed payload never enters the\n * transport — the worker MUST also re-validate from Postgres but defense in\n * depth is cheap.\n */\nexport function assertServerGenerationJobPayload(\n candidate: unknown,\n): ServerGenerationJobPayload {\n const result = ServerGenerationJobPayloadSchema.safeParse(candidate);\n if (!result.success) {\n throw new ServerGenerationJobPayloadValidationError(result.error.issues);\n }\n return result.data as ServerGenerationJobPayload;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JobsOptions } from 'bullmq';\nimport type {\n GenerateObservationsForEventJob,\n GenerateSessionSummaryJob,\n} from '../jobs/types.js';\nimport { buildServerJobId } from '../jobs/job-id.js';\nimport type { PostgresAgentEvent } from '../../storage/postgres/agent-events.js';\nimport type { PostgresObservationGenerationJob } from '../../storage/postgres/generation-jobs.js';\n\n// SessionGenerationPolicy decides WHEN to enqueue work for the BullMQ event\n// and summary lanes. It is configurable via:\n// - CLAUDE_MEM_SERVER_SESSION_POLICY env var (per-process default)\n// - per-call override (per-team settings can plug in here later)\n//\n// Three policies are supported:\n// - 'per-event' (default): enqueue immediately on every event POST.\n// Matches Phase 4/5 behavior.\n// - 'debounce': enqueue with `delay`; when a new event arrives within\n// the window, replace the delayed job (deterministic\n// BullMQ jobId means re-add(jobId, ...) overwrites the\n// waiting entry, and removeOnComplete/Fail keep things\n// tidy). Outbox row is canonical so durability is safe.\n// - 'end-of-session': only enqueue summary jobs at /v1/sessions/:id/end.\n// Per-event posts skip BullMQ entirely; the outbox row\n// remains in `queued` state and startup reconciliation\n// will publish it later (or it can be cancelled).\n//\n// Anti-pattern guard: the policy MUST NOT use ActiveSession-style cached\n// state. Inputs are always reloaded by the caller from Postgres before this\n// fires.\n\nexport type ServerSessionGenerationPolicy = 'per-event' | 'debounce' | 'end-of-session';\n\nconst DEFAULT_DEBOUNCE_MS = 5000;\n\nexport interface SessionGenerationPolicyOptions {\n policy?: ServerSessionGenerationPolicy;\n debounceWindowMs?: number;\n}\n\nexport function resolveSessionGenerationPolicy(\n options: SessionGenerationPolicyOptions = {},\n): { policy: ServerSessionGenerationPolicy; debounceWindowMs: number } {\n const envPolicy = (process.env.CLAUDE_MEM_SERVER_SESSION_POLICY ?? '').trim().toLowerCase();\n const policy: ServerSessionGenerationPolicy = options.policy\n ?? (envPolicy === 'debounce' || envPolicy === 'end-of-session' || envPolicy === 'per-event'\n ? envPolicy\n : 'per-event');\n const debounceWindowMs = options.debounceWindowMs\n ?? (Number.parseInt(process.env.CLAUDE_MEM_SERVER_SESSION_DEBOUNCE_MS ?? '', 10)\n || DEFAULT_DEBOUNCE_MS);\n return {\n policy,\n debounceWindowMs: Number.isFinite(debounceWindowMs) && debounceWindowMs > 0\n ? debounceWindowMs\n : DEFAULT_DEBOUNCE_MS,\n };\n}\n\nexport interface EnqueueEventDecisionInput {\n event: PostgresAgentEvent;\n outbox: PostgresObservationGenerationJob;\n // Phase 11 — identity context captured at HTTP ingest time so the BullMQ\n // payload carries every audit field. apiKeyId may be null for local-dev\n // enqueues and `actorId` follows the api key's `actor_id` column.\n apiKeyId?: string | null;\n actorId?: string | null;\n sourceAdapter?: string | null;\n // Phase 12 — request correlation id minted at the HTTP boundary.\n requestId?: string | null;\n}\n\nexport interface EnqueueEventDecision {\n shouldEnqueue: boolean;\n jobId: string;\n payload: GenerateObservationsForEventJob;\n jobsOptions?: JobsOptions;\n reason: 'per-event' | 'debounce' | 'end-of-session-skip';\n}\n\nexport function buildEnqueueEventDecision(\n input: EnqueueEventDecisionInput,\n options: SessionGenerationPolicyOptions = {},\n): EnqueueEventDecision {\n const resolved = resolveSessionGenerationPolicy(options);\n const jobId = input.outbox.bullmqJobId ?? buildServerJobId({\n kind: 'event',\n team_id: input.event.teamId,\n project_id: input.event.projectId,\n source_type: 'agent_event',\n source_id: input.event.id,\n });\n const payload: GenerateObservationsForEventJob = {\n kind: 'event',\n team_id: input.outbox.teamId,\n project_id: input.outbox.projectId,\n source_type: 'agent_event',\n source_id: input.event.id,\n generation_job_id: input.outbox.id,\n agent_event_id: input.event.id,\n api_key_id: input.apiKeyId ?? null,\n actor_id: input.actorId ?? null,\n source_adapter: input.sourceAdapter ?? input.event.sourceAdapter ?? 'api',\n request_id: input.requestId ?? null,\n };\n\n if (resolved.policy === 'end-of-session') {\n return { shouldEnqueue: false, jobId, payload, reason: 'end-of-session-skip' };\n }\n\n if (resolved.policy === 'debounce') {\n return {\n shouldEnqueue: true,\n jobId,\n payload,\n jobsOptions: { delay: resolved.debounceWindowMs },\n reason: 'debounce',\n };\n }\n\n return { shouldEnqueue: true, jobId, payload, reason: 'per-event' };\n}\n\n// Minimal queue surface used by scheduleDebouncedEventJob. Declared as an\n// interface (instead of `Pick<ServerJobQueue<...>, ...>`) so the parameter\n// accepts ServerJobQueue<ServerGenerationJobPayload> at the call site without\n// triggering invariant TPayload type errors. The ServerJobQueue.add signature\n// is structurally compatible — it requires `payload: TPayload`, and we only\n// hand in narrowed payloads.\nexport interface DebounceableEventQueue {\n add(jobId: string, payload: GenerateObservationsForEventJob, options?: JobsOptions): Promise<void>;\n remove(jobId: string): Promise<void>;\n getJob(jobId: string): Promise<unknown>;\n}\n\n/**\n * Apply a debounce decision to a BullMQ queue. If a delayed job already exists\n * for this deterministic id, BullMQ's `add(jobId, ...)` will be a no-op, so we\n * proactively remove it first so the new event's delay window starts fresh.\n *\n * This implements the \"if a new event arrives within window, replace the\n * delayed job\" requirement.\n */\nexport async function scheduleDebouncedEventJob(\n queue: DebounceableEventQueue,\n decision: EnqueueEventDecision,\n): Promise<void> {\n if (!decision.shouldEnqueue) return;\n if (decision.reason === 'debounce') {\n try {\n const existing = await queue.getJob(decision.jobId);\n if (existing) {\n await queue.remove(decision.jobId);\n }\n } catch {\n // best-effort; if remove fails because the job already moved to active\n // we just let `add` no-op or fail through to the caller's error handler\n }\n }\n await queue.add(decision.jobId, decision.payload, decision.jobsOptions);\n}\n\nexport interface BuildSummaryJobInput {\n serverSessionId: string;\n teamId: string;\n projectId: string;\n generationJobId: string;\n // Phase 11 — same identity context the event-payload builder receives.\n apiKeyId?: string | null;\n actorId?: string | null;\n sourceAdapter?: string | null;\n // Phase 12 — request correlation id flows into the summary lane too.\n requestId?: string | null;\n}\n\nexport function buildSummaryJobId(input: {\n serverSessionId: string;\n teamId: string;\n projectId: string;\n}): string {\n return buildServerJobId({\n kind: 'summary',\n team_id: input.teamId,\n project_id: input.projectId,\n source_type: 'session_summary',\n source_id: input.serverSessionId,\n });\n}\n\nexport function buildSummaryJobPayload(input: BuildSummaryJobInput): GenerateSessionSummaryJob {\n return {\n kind: 'summary',\n team_id: input.teamId,\n project_id: input.projectId,\n source_type: 'session_summary',\n source_id: input.serverSessionId,\n generation_job_id: input.generationJobId,\n server_session_id: input.serverSessionId,\n api_key_id: input.apiKeyId ?? null,\n actor_id: input.actorId ?? null,\n source_adapter: input.sourceAdapter ?? 'api',\n request_id: input.requestId ?? null,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\n// Shared event-ingest path used by both `/v1/events` (canonical) and\n// `src/server/compat/SessionsObservationsAdapter.ts` (legacy translator).\n// Centralizes the transactional write (event row + outbox row + lifecycle\n// log) and the post-commit BullMQ enqueue so both call sites apply the\n// exact same SessionGenerationPolicy and outbox-then-publish guarantees.\n//\n// This module MUST NOT import from src/services/worker/* — the whole point\n// of Phase 9 is to give the compat adapters a translation surface that\n// reaches Server beta core directly, with no worker-layer detours.\n\nimport type { CreatePostgresAgentEventInput, PostgresAgentEvent } from '../../storage/postgres/agent-events.js';\nimport { PostgresAgentEventsRepository } from '../../storage/postgres/agent-events.js';\nimport {\n PostgresObservationGenerationJobEventsRepository,\n PostgresObservationGenerationJobRepository,\n type PostgresObservationGenerationJob,\n} from '../../storage/postgres/generation-jobs.js';\nimport type { PostgresPool } from '../../storage/postgres/pool.js';\nimport { withPostgresTransaction } from '../../storage/postgres/pool.js';\nimport { logger } from '../../utils/logger.js';\nimport { buildServerJobId } from '../jobs/job-id.js';\nimport type { GenerateObservationsForEventJob } from '../jobs/types.js';\nimport {\n buildEnqueueEventDecision,\n scheduleDebouncedEventJob,\n type ServerSessionGenerationPolicy,\n} from '../runtime/SessionGenerationPolicy.js';\nimport { newId } from '../../storage/postgres/utils.js';\n\nfunction buildEventBullmqPayload(input: {\n outboxId: string;\n event: PostgresAgentEvent;\n apiKeyId: string | null;\n actorId: string | null;\n sourceAdapter: string | null;\n requestId: string | null;\n}): GenerateObservationsForEventJob {\n return {\n kind: 'event',\n team_id: input.event.teamId,\n project_id: input.event.projectId,\n source_type: 'agent_event',\n source_id: input.event.id,\n generation_job_id: input.outboxId,\n agent_event_id: input.event.id,\n api_key_id: input.apiKeyId,\n actor_id: input.actorId,\n source_adapter: input.sourceAdapter ?? input.event.sourceAdapter ?? 'api',\n request_id: input.requestId,\n };\n}\n\nconst EVENT_JOB_TYPE = 'observation_generate_for_event';\n\nexport type EnqueueOutcome = 'enqueued' | 'queued_only' | 'skipped';\n\nexport interface IngestEventsServiceOptions {\n pool: PostgresPool;\n // Lazy queue resolver so the service does not depend on the queue manager\n // type and tests can swap in a fake. When this returns null, the outbox\n // row stays `queued` and Phase 3 startup reconciliation will publish it.\n resolveEventQueue: () => EventQueueLike | null;\n sessionPolicy?: ServerSessionGenerationPolicy;\n}\n\nexport interface EventQueueLike {\n add(jobId: string, payload: unknown, options?: unknown): Promise<unknown>;\n}\n\nexport interface IngestEventResult {\n event: PostgresAgentEvent;\n outbox: PostgresObservationGenerationJob | null;\n enqueueState: EnqueueOutcome;\n}\n\nexport interface IngestEventOptions {\n generate?: boolean;\n source?: string;\n // Phase 11 — identity context that flows from the HTTP auth boundary into\n // the BullMQ payload and audit log. None of these are auth gates: the\n // worker reloads and re-validates from Postgres before any side effect.\n apiKeyId?: string | null;\n actorId?: string | null;\n sourceAdapter?: string | null;\n // Phase 12 — opaque correlation id minted at the HTTP middleware so\n // generator logs and audit rows can pivot back to the originating request.\n requestId?: string | null;\n}\n\nexport class IngestEventsService {\n constructor(private readonly options: IngestEventsServiceOptions) {}\n\n async ingestOne(\n input: CreatePostgresAgentEventInput,\n opts: IngestEventOptions = {},\n ): Promise<IngestEventResult> {\n const generate = opts.generate ?? true;\n const source = opts.source ?? 'http_post_v1_events';\n\n const txResult = await withPostgresTransaction(this.options.pool, async (client) => {\n const eventsRepo = new PostgresAgentEventsRepository(client);\n const inserted = await eventsRepo.create(input);\n\n if (!generate) {\n return { event: inserted, outbox: null as PostgresObservationGenerationJob | null };\n }\n\n const jobsRepo = new PostgresObservationGenerationJobRepository(client);\n const eventsLogRepo = new PostgresObservationGenerationJobEventsRepository(client);\n // Pre-generate the outbox id so we can build the BullMQ payload (which\n // references generation_job_id) and persist it on the row. Reconciliation\n // and operator retry rely on this persisted payload to re-enqueue a\n // payload that passes assertServerGenerationJobPayload at the worker.\n const outboxId = newId();\n const bullmqPayload = buildEventBullmqPayload({\n outboxId,\n event: inserted,\n apiKeyId: opts.apiKeyId ?? null,\n actorId: opts.actorId ?? null,\n sourceAdapter: opts.sourceAdapter ?? null,\n requestId: opts.requestId ?? null,\n });\n const outbox = await jobsRepo.create({\n id: outboxId,\n projectId: inserted.projectId,\n teamId: inserted.teamId,\n sourceType: 'agent_event',\n sourceId: inserted.id,\n agentEventId: inserted.id,\n serverSessionId: inserted.serverSessionId,\n jobType: EVENT_JOB_TYPE,\n bullmqJobId: buildServerJobId({\n kind: 'event',\n team_id: inserted.teamId,\n project_id: inserted.projectId,\n source_type: 'agent_event',\n source_id: inserted.id,\n }),\n payload: bullmqPayload as unknown as Record<string, unknown>,\n });\n await eventsLogRepo.append({\n generationJobId: outbox.id,\n projectId: outbox.projectId,\n teamId: outbox.teamId,\n eventType: 'queued',\n statusAfter: outbox.status,\n attempt: outbox.attempts,\n details: { source },\n });\n return { event: inserted, outbox };\n });\n\n let enqueueState: EnqueueOutcome = 'skipped';\n if (txResult.outbox) {\n enqueueState = await this.publishEventJob(txResult.event, txResult.outbox, opts);\n }\n return { event: txResult.event, outbox: txResult.outbox, enqueueState };\n }\n\n async ingestBatch(\n inputs: CreatePostgresAgentEventInput[],\n opts: IngestEventOptions = {},\n ): Promise<IngestEventResult[]> {\n const generate = opts.generate ?? true;\n const source = opts.source ?? 'http_post_v1_events_batch';\n\n const txResults = await withPostgresTransaction(this.options.pool, async (client) => {\n const eventsRepo = new PostgresAgentEventsRepository(client);\n const jobsRepo = new PostgresObservationGenerationJobRepository(client);\n const eventsLogRepo = new PostgresObservationGenerationJobEventsRepository(client);\n const acc: { event: PostgresAgentEvent; outbox: PostgresObservationGenerationJob | null }[] = [];\n for (const input of inputs) {\n const event = await eventsRepo.create(input);\n if (!generate) {\n acc.push({ event, outbox: null });\n continue;\n }\n const outboxId = newId();\n const bullmqPayload = buildEventBullmqPayload({\n outboxId,\n event,\n apiKeyId: opts.apiKeyId ?? null,\n actorId: opts.actorId ?? null,\n sourceAdapter: opts.sourceAdapter ?? null,\n requestId: opts.requestId ?? null,\n });\n const outbox = await jobsRepo.create({\n id: outboxId,\n projectId: event.projectId,\n teamId: event.teamId,\n sourceType: 'agent_event',\n sourceId: event.id,\n agentEventId: event.id,\n serverSessionId: event.serverSessionId,\n jobType: EVENT_JOB_TYPE,\n bullmqJobId: buildServerJobId({\n kind: 'event',\n team_id: event.teamId,\n project_id: event.projectId,\n source_type: 'agent_event',\n source_id: event.id,\n }),\n payload: bullmqPayload as unknown as Record<string, unknown>,\n });\n await eventsLogRepo.append({\n generationJobId: outbox.id,\n projectId: outbox.projectId,\n teamId: outbox.teamId,\n eventType: 'queued',\n statusAfter: outbox.status,\n attempt: outbox.attempts,\n details: { source },\n });\n acc.push({ event, outbox });\n }\n return acc;\n });\n\n return Promise.all(txResults.map(async ({ event, outbox }) => {\n const enqueueState: EnqueueOutcome = outbox\n ? await this.publishEventJob(event, outbox, opts)\n : 'skipped';\n return { event, outbox, enqueueState };\n }));\n }\n\n private async publishEventJob(\n event: PostgresAgentEvent,\n outbox: PostgresObservationGenerationJob,\n opts: IngestEventOptions = {},\n ): Promise<'enqueued' | 'queued_only'> {\n const queue = this.options.resolveEventQueue();\n if (!queue) {\n return 'queued_only';\n }\n const policyOptions: { policy?: ServerSessionGenerationPolicy; debounceWindowMs?: number } = {};\n if (this.options.sessionPolicy !== undefined) {\n policyOptions.policy = this.options.sessionPolicy;\n }\n const decision = buildEnqueueEventDecision(\n {\n event,\n outbox,\n apiKeyId: opts.apiKeyId ?? null,\n actorId: opts.actorId ?? null,\n sourceAdapter: opts.sourceAdapter ?? event.sourceAdapter ?? null,\n // Phase 12 — flow request_id into the BullMQ payload so the worker\n // can emit it in [generation] logs and the audit row.\n requestId: opts.requestId ?? null,\n },\n policyOptions,\n );\n if (!decision.shouldEnqueue) {\n return 'queued_only';\n }\n try {\n await scheduleDebouncedEventJob(queue as never, decision);\n return 'enqueued';\n } catch (error) {\n logger.warn('SYSTEM', 'failed to publish event generation job to BullMQ', {\n outboxId: outbox.id,\n error: error instanceof Error ? error.message : String(error),\n });\n return 'queued_only';\n }\n }\n}\n","\nimport { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport type { ModeConfig, ObservationType } from './types.js';\nimport { logger } from '../../utils/logger.js';\nimport { getPackageRoot } from '../../shared/paths.js';\n\nexport class ModeManager {\n private static instance: ModeManager | null = null;\n private activeMode: ModeConfig | null = null;\n private modesDir: string;\n\n private constructor() {\n const packageRoot = getPackageRoot();\n \n const possiblePaths = [\n ...(process.env.CLAUDE_MEM_MODES_DIR ? [process.env.CLAUDE_MEM_MODES_DIR] : []),\n join(packageRoot, 'modes'), // Production (plugin/modes)\n join(packageRoot, '..', 'plugin', 'modes'), // Development (src/../plugin/modes)\n ];\n\n const foundPath = possiblePaths.find(p => existsSync(p));\n this.modesDir = foundPath || possiblePaths[0];\n }\n\n static getInstance(): ModeManager {\n if (!ModeManager.instance) {\n ModeManager.instance = new ModeManager();\n }\n return ModeManager.instance;\n }\n\n private parseInheritance(modeId: string): {\n hasParent: boolean;\n parentId: string;\n overrideId: string;\n } {\n const parts = modeId.split('--');\n\n if (parts.length === 1) {\n return { hasParent: false, parentId: '', overrideId: '' };\n }\n\n if (parts.length > 2) {\n throw new Error(\n `Invalid mode inheritance: ${modeId}. Only one level of inheritance supported (parent--override)`\n );\n }\n\n return {\n hasParent: true,\n parentId: parts[0],\n overrideId: modeId \n };\n }\n\n private isPlainObject(value: unknown): boolean {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value)\n );\n }\n\n private deepMerge<T>(base: T, override: Partial<T>): T {\n const result = { ...base } as T;\n\n for (const key in override) {\n const overrideValue = override[key];\n const baseValue = base[key];\n\n if (this.isPlainObject(overrideValue) && this.isPlainObject(baseValue)) {\n result[key] = this.deepMerge(baseValue, overrideValue as any);\n } else {\n result[key] = overrideValue as T[Extract<keyof T, string>];\n }\n }\n\n return result;\n }\n\n private loadModeFile(modeId: string): ModeConfig {\n const modePath = join(this.modesDir, `${modeId}.json`);\n\n if (!existsSync(modePath)) {\n throw new Error(`Mode file not found: ${modePath}`);\n }\n\n const jsonContent = readFileSync(modePath, 'utf-8');\n return JSON.parse(jsonContent) as ModeConfig;\n }\n\n loadMode(modeId: string): ModeConfig {\n const inheritance = this.parseInheritance(modeId);\n\n if (!inheritance.hasParent) {\n try {\n const mode = this.loadModeFile(modeId);\n this.activeMode = mode;\n logger.debug('SYSTEM', `Loaded mode: ${mode.name} (${modeId})`, undefined, {\n types: mode.observation_types.map(t => t.id),\n concepts: mode.observation_concepts.map(c => c.id)\n });\n return mode;\n } catch (error) {\n if (error instanceof Error) {\n logger.warn('WORKER', `Mode file not found: ${modeId}, falling back to 'code'`, { message: error.message });\n } else {\n logger.warn('WORKER', `Mode file not found: ${modeId}, falling back to 'code'`, { error: String(error) });\n }\n if (modeId === 'code') {\n throw new Error('Critical: code.json mode file missing');\n }\n return this.loadMode('code');\n }\n }\n\n const { parentId, overrideId } = inheritance;\n\n let parentMode: ModeConfig;\n try {\n parentMode = this.loadMode(parentId);\n } catch (error) {\n if (error instanceof Error) {\n logger.warn('WORKER', `Parent mode '${parentId}' not found for ${modeId}, falling back to 'code'`, { message: error.message });\n } else {\n logger.warn('WORKER', `Parent mode '${parentId}' not found for ${modeId}, falling back to 'code'`, { error: String(error) });\n }\n parentMode = this.loadMode('code');\n }\n\n let overrideConfig: Partial<ModeConfig>;\n try {\n overrideConfig = this.loadModeFile(overrideId);\n logger.debug('SYSTEM', `Loaded override file: ${overrideId} for parent ${parentId}`);\n } catch (error) {\n if (error instanceof Error) {\n logger.warn('WORKER', `Override file '${overrideId}' not found, using parent mode '${parentId}' only`, { message: error.message });\n } else {\n logger.warn('WORKER', `Override file '${overrideId}' not found, using parent mode '${parentId}' only`, { error: String(error) });\n }\n this.activeMode = parentMode;\n return parentMode;\n }\n\n if (!overrideConfig) {\n logger.warn('SYSTEM', `Invalid override file: ${overrideId}, using parent mode '${parentId}' only`);\n this.activeMode = parentMode;\n return parentMode;\n }\n\n const mergedMode = this.deepMerge(parentMode, overrideConfig);\n this.activeMode = mergedMode;\n\n logger.debug('SYSTEM', `Loaded mode with inheritance: ${mergedMode.name} (${modeId} = ${parentId} + ${overrideId})`, undefined, {\n parent: parentId,\n override: overrideId,\n types: mergedMode.observation_types.map(t => t.id),\n concepts: mergedMode.observation_concepts.map(c => c.id)\n });\n\n return mergedMode;\n }\n\n getActiveMode(): ModeConfig {\n if (!this.activeMode) {\n throw new Error('No mode loaded. Call loadMode() first.');\n }\n return this.activeMode;\n }\n\n getObservationTypes(): ObservationType[] {\n return this.getActiveMode().observation_types;\n }\n\n getTypeIcon(typeId: string): string {\n const type = this.getObservationTypes().find(t => t.id === typeId);\n return type?.emoji || '📝';\n }\n\n getWorkEmoji(typeId: string): string {\n const type = this.getObservationTypes().find(t => t.id === typeId);\n return type?.work_emoji || '📝';\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n\n// Server-beta-local copy of the worker provider error classification model.\n// Phase 5 anti-pattern guard: src/server/* must not import from\n// src/services/worker/*, so we duplicate the small, stable error model here.\n// Worker code keeps src/services/worker/provider-errors.ts unchanged.\n\nexport type ServerProviderErrorClass =\n | 'transient'\n | 'unrecoverable'\n | 'rate_limit'\n | 'quota_exhausted'\n | 'auth_invalid'\n | 'parse_error'\n | (string & {});\n\nexport class ServerClassifiedProviderError extends Error {\n readonly kind: ServerProviderErrorClass;\n readonly retryAfterMs?: number;\n readonly cause: unknown;\n\n constructor(\n message: string,\n opts: {\n kind: ServerProviderErrorClass;\n cause: unknown;\n retryAfterMs?: number;\n },\n ) {\n super(message);\n this.name = 'ServerClassifiedProviderError';\n this.kind = opts.kind;\n this.cause = opts.cause;\n if (opts.retryAfterMs !== undefined) {\n this.retryAfterMs = opts.retryAfterMs;\n }\n }\n}\n\n/**\n * Parse Retry-After header (seconds or HTTP-date). Returns ms or undefined.\n * Behavior intentionally mirrors the worker providers' helper so server\n * retries match worker retry policy.\n */\nexport function parseRetryAfterMs(value: string | null): number | undefined {\n if (!value) return undefined;\n const seconds = Number(value);\n if (!Number.isNaN(seconds) && seconds >= 0) {\n return Math.floor(seconds * 1000);\n }\n const dateMs = Date.parse(value);\n if (!Number.isNaN(dateMs)) {\n const delta = dateMs - Date.now();\n return delta > 0 ? delta : 0;\n }\n return undefined;\n}\n\ninterface ClassifyHttpInput {\n status?: number;\n bodyText?: string;\n headers?: Headers | { get(name: string): string | null };\n cause: unknown;\n providerLabel: string;\n}\n\n/**\n * Generic HTTP-error → ServerClassifiedProviderError mapping shared by\n * Gemini and OpenRouter server adapters. Provider-specific overrides (e.g.\n * Anthropic OverloadedError, Gemini quota body markers) are layered on top\n * by the per-provider classifier wrappers in this module.\n */\nexport function classifyHttpProviderError(input: ClassifyHttpInput): ServerClassifiedProviderError {\n const { status, providerLabel } = input;\n const body = input.bodyText ?? '';\n const lower = body.toLowerCase();\n const retryAfterMs = input.headers ? parseRetryAfterMs(input.headers.get('retry-after')) : undefined;\n\n if (\n lower.includes('quota exceeded') ||\n lower.includes('insufficient credits') ||\n lower.includes('insufficient_quota') ||\n lower.includes('resource_exhausted')\n ) {\n return new ServerClassifiedProviderError(\n `${providerLabel} quota exhausted${status !== undefined ? ` (status ${status})` : ''}`,\n { kind: 'quota_exhausted', cause: input.cause },\n );\n }\n\n if (status === 429) {\n return new ServerClassifiedProviderError(`${providerLabel} rate limit (429)`, {\n kind: 'rate_limit',\n cause: input.cause,\n ...(retryAfterMs !== undefined ? { retryAfterMs } : {}),\n });\n }\n\n if (status === 401 || status === 403) {\n return new ServerClassifiedProviderError(`${providerLabel} auth error (status ${status})`, {\n kind: 'auth_invalid',\n cause: input.cause,\n });\n }\n\n if (status === 400 || status === 404) {\n return new ServerClassifiedProviderError(`${providerLabel} bad request (status ${status})`, {\n kind: 'unrecoverable',\n cause: input.cause,\n });\n }\n\n if (status !== undefined && status >= 500 && status < 600) {\n return new ServerClassifiedProviderError(`${providerLabel} upstream error (status ${status})`, {\n kind: 'transient',\n cause: input.cause,\n });\n }\n\n if (status === undefined) {\n const message = input.cause instanceof Error ? input.cause.message : String(input.cause);\n return new ServerClassifiedProviderError(`${providerLabel} network error: ${message}`, {\n kind: 'transient',\n cause: input.cause,\n });\n }\n\n return new ServerClassifiedProviderError(\n `${providerLabel} API error: ${status}${body ? ` - ${body.substring(0, 200)}` : ''}`,\n { kind: 'unrecoverable', cause: input.cause },\n );\n}\n","\nimport { logger } from './logger.js';\n\nconst TAG_NAMES = [\n 'private',\n 'claude-mem-context',\n 'system_instruction',\n 'system-instruction',\n 'persisted-output',\n 'system-reminder',\n] as const;\ntype TagName = (typeof TAG_NAMES)[number];\n\nconst STRIP_REGEX = new RegExp(\n `<(${TAG_NAMES.join('|')})\\\\b[^>]*>[\\\\s\\\\S]*?</\\\\1>`,\n 'g'\n);\n\nexport const SYSTEM_REMINDER_REGEX = /<system-reminder>[\\s\\S]*?<\\/system-reminder>/g;\n\nconst MAX_TAG_COUNT = 100;\n\nexport function stripTags(input: string): { stripped: string; counts: Record<TagName, number> } {\n const counts: Record<TagName, number> = Object.fromEntries(\n TAG_NAMES.map(name => [name, 0])\n ) as Record<TagName, number>;\n\n STRIP_REGEX.lastIndex = 0; \n\n let total = 0;\n const stripped = input.replace(STRIP_REGEX, (_, name: TagName) => {\n counts[name] = (counts[name] ?? 0) + 1;\n total += 1;\n return '';\n });\n\n if (total > MAX_TAG_COUNT) {\n logger.warn('SYSTEM', 'tag count exceeds limit', undefined, {\n tagCount: total,\n maxAllowed: MAX_TAG_COUNT,\n contentLength: input.length,\n });\n }\n\n return { stripped: stripped.trim(), counts };\n}\n\nexport function stripMemoryTagsFromJson(content: string): string {\n return stripTags(content).stripped;\n}\n\nexport function stripMemoryTagsFromPrompt(content: string): string {\n return stripTags(content).stripped;\n}\n\nconst PROTOCOL_ONLY_TAGS = ['task-notification'] as const;\n\nconst PROTOCOL_ONLY_REGEX = new RegExp(\n `^\\\\s*<(${PROTOCOL_ONLY_TAGS.join('|')})\\\\b[^>]*>(?:(?!<\\\\1\\\\b|</\\\\1\\\\b)[\\\\s\\\\S])*</\\\\1>\\\\s*$`,\n);\n\nconst MAX_PROTOCOL_PAYLOAD_BYTES = 256 * 1024;\n\nexport function isInternalProtocolPayload(text: string): boolean {\n if (!text) return false;\n if (text.length > MAX_PROTOCOL_PAYLOAD_BYTES) return false;\n return PROTOCOL_ONLY_REGEX.test(text);\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { ModeManager } from '../../../../services/domain/ModeManager.js';\nimport type { ModeConfig, ObservationType } from '../../../../services/domain/types.js';\nimport { stripTags } from '../../../../utils/tag-stripping.js';\nimport type { PostgresAgentEvent } from '../../../../storage/postgres/agent-events.js';\nimport type { ServerGenerationContext } from './types.js';\n\n// Fallback list mirrors the default observation types used by claude-mem\n// modes. The server-beta prompt does not strictly need a loaded mode file —\n// the parser accepts any of these as the <type> value — so when no mode is\n// loaded (tests, fresh installs) we synthesize a minimal type list rather\n// than throwing.\nconst FALLBACK_OBSERVATION_TYPES: ReadonlyArray<Pick<ObservationType, 'id'>> = [\n { id: 'discovery' },\n { id: 'progress' },\n { id: 'blocker' },\n { id: 'decision' },\n];\n\n// Build a single-shot generation prompt from a list of AgentEvent records\n// plus project/session metadata. Output: a user prompt asking the provider\n// to return one or more <observation> XML blocks (or an empty response if\n// the batch should be skipped). This is intentionally a single-turn request\n// — server-beta does NOT use the worker's multi-turn SDK conversation\n// model. parseAgentXml(...) accepts the response unchanged.\n//\n// Privacy: every event payload field passes through `stripTags` (which\n// removes <private>, <claude-mem-context>, <system-reminder>, etc.) before\n// being included in the prompt. Privacy enforcement here is belt-and-suspenders\n// — `processGeneratedResponse` also discards observations that are entirely\n// derived from privately-tagged inputs.\n\nexport interface BuildServerPromptResult {\n readonly prompt: string;\n readonly hadPrivateContent: boolean;\n readonly skippedAll: boolean;\n}\n\nconst MAX_PAYLOAD_CHARS = 16 * 1024;\n\nexport function buildServerGenerationPrompt(\n context: ServerGenerationContext,\n options: { mode?: ModeConfig } = {},\n): BuildServerPromptResult {\n const mode = options.mode ?? loadActiveModeOrFallback();\n\n let hadPrivateContent = false;\n let allEventsScrubbedToEmpty = true;\n const eventBlocks: string[] = [];\n\n for (const event of context.events) {\n const block = buildEventBlock(event);\n if (block.hadPrivate) {\n hadPrivateContent = true;\n }\n if (block.body.length > 0) {\n allEventsScrubbedToEmpty = false;\n eventBlocks.push(block.body);\n }\n }\n\n const skippedAll = context.events.length > 0 && allEventsScrubbedToEmpty;\n\n const sessionTag = context.project.serverSessionId\n ? `\\n <server_session_id>${escapeXml(context.project.serverSessionId)}</server_session_id>`\n : '';\n const projectTag = context.project.projectName\n ? `\\n <project_name>${escapeXml(context.project.projectName)}</project_name>`\n : '';\n\n const observationOutputSchema = buildObservationOutputSchema(mode);\n\n const prompt = [\n '<server_beta_observation_request>',\n ` <project_id>${escapeXml(context.project.projectId)}</project_id>`,\n ` <team_id>${escapeXml(context.project.teamId)}</team_id>` + sessionTag + projectTag,\n ` <generation_job_id>${escapeXml(context.job.id)}</generation_job_id>`,\n ' <agent_events>',\n eventBlocks.length > 0 ? eventBlocks.join('\\n') : ' <!-- empty after privacy stripping -->',\n ' </agent_events>',\n '</server_beta_observation_request>',\n '',\n 'You are observing an agent at work. Return one or more',\n '<observation>...</observation> XML blocks summarizing durable, useful',\n 'discoveries from the events above. If the events contain nothing worth',\n 'recording (e.g., everything was scrubbed by privacy filters or the',\n 'activity was trivial), return a single self-closing <skip_summary />',\n 'tag and nothing else. Do not include any prose outside the XML.',\n '',\n 'Schema for each <observation> block:',\n observationOutputSchema,\n ].join('\\n');\n\n return { prompt, hadPrivateContent, skippedAll };\n}\n\ninterface EventBlockResult {\n body: string;\n hadPrivate: boolean;\n}\n\nfunction buildEventBlock(event: PostgresAgentEvent): EventBlockResult {\n const rawPayload =\n typeof event.payload === 'string' ? event.payload : JSON.stringify(event.payload ?? {}, null, 2);\n\n const stripResult = stripTags(rawPayload);\n const hadPrivate = (stripResult.counts.private ?? 0) > 0;\n const truncatedPayload = stripResult.stripped.length > MAX_PAYLOAD_CHARS\n ? stripResult.stripped.slice(0, MAX_PAYLOAD_CHARS) + '\\n[...truncated]'\n : stripResult.stripped;\n\n if (truncatedPayload.trim().length === 0) {\n return { body: '', hadPrivate };\n }\n\n return {\n body: [\n ' <agent_event>',\n ` <id>${escapeXml(event.id)}</id>`,\n ` <event_type>${escapeXml(event.eventType)}</event_type>`,\n ` <source_adapter>${escapeXml(event.sourceAdapter)}</source_adapter>`,\n ` <occurred_at>${new Date(event.occurredAtEpoch).toISOString()}</occurred_at>`,\n ' <payload>',\n escapeXml(truncatedPayload),\n ' </payload>',\n ' </agent_event>',\n ].join('\\n'),\n hadPrivate,\n };\n}\n\nfunction loadActiveModeOrFallback(): ModeConfig | { observation_types: ReadonlyArray<Pick<ObservationType, 'id'>> } {\n try {\n return ModeManager.getInstance().getActiveMode();\n } catch {\n return { observation_types: FALLBACK_OBSERVATION_TYPES } as unknown as ModeConfig;\n }\n}\n\nfunction buildObservationOutputSchema(mode: ModeConfig | { observation_types: ReadonlyArray<Pick<ObservationType, 'id'>> }): string {\n const types = mode.observation_types.map(t => t.id).join(' | ');\n return [\n '<observation>',\n ` <type>[ ${types} ]</type>`,\n ' <title>...</title>',\n ' <subtitle>...</subtitle>',\n ' <facts><fact>...</fact></facts>',\n ' <narrative>...</narrative>',\n ' <concepts><concept>...</concept></concepts>',\n ' <files_read><file>...</file></files_read>',\n ' <files_modified><file>...</file></files_modified>',\n '</observation>',\n ].join('\\n');\n}\n\nfunction escapeXml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;');\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { logger } from '../../../utils/logger.js';\nimport {\n ServerClassifiedProviderError,\n parseRetryAfterMs,\n} from './shared/error-classification.js';\nimport { buildServerGenerationPrompt } from './shared/prompt-builder.js';\nimport type {\n ServerGenerationContext,\n ServerGenerationProvider,\n ServerGenerationResult,\n} from './shared/types.js';\n\nconst ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';\nconst ANTHROPIC_VERSION = '2023-06-01';\n// #2554 — the previous default `claude-3-5-sonnet-latest` is stale and 404s on\n// the current Anthropic Messages API. Align with the repo's canonical default\n// model (CLAUDE_MEM_MODEL in src/ui/viewer/constants/settings.ts and the\n// installer's model list) so a server with no explicit CLAUDE_MEM_SERVER_MODEL\n// generates against a valid model id instead of failing every job with a 404.\nexport const DEFAULT_SERVER_CLAUDE_MODEL = 'claude-sonnet-4-6';\nconst DEFAULT_MODEL = DEFAULT_SERVER_CLAUDE_MODEL;\n\nexport interface ClaudeObservationProviderOptions {\n apiKey: string;\n model?: string;\n maxOutputTokens?: number;\n fetchImpl?: typeof fetch;\n}\n\ninterface AnthropicMessagesResponse {\n content?: Array<{ type?: string; text?: string }>;\n usage?: { input_tokens?: number; output_tokens?: number };\n error?: { type?: string; message?: string };\n}\n\nexport class ClaudeObservationProvider implements ServerGenerationProvider {\n readonly providerLabel = 'claude' as const;\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxOutputTokens: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: ClaudeObservationProviderOptions) {\n if (!options.apiKey) {\n throw new ServerClassifiedProviderError('Anthropic API key not configured', {\n kind: 'auth_invalid',\n cause: new Error('apiKey is required'),\n });\n }\n this.apiKey = options.apiKey;\n this.model = options.model ?? DEFAULT_MODEL;\n this.maxOutputTokens = options.maxOutputTokens ?? 4096;\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async generate(\n context: ServerGenerationContext,\n signal?: AbortSignal,\n ): Promise<ServerGenerationResult> {\n const { prompt, skippedAll } = buildServerGenerationPrompt(context);\n if (skippedAll) {\n // All events were scrubbed by privacy stripping. Don't bill the\n // provider — return a synthetic skip response that parser accepts.\n return {\n rawText: '<skip_summary reason=\"all_events_private\" />',\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n\n let response: Response;\n try {\n response = await this.fetchImpl(ANTHROPIC_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': ANTHROPIC_VERSION,\n },\n body: JSON.stringify({\n model: this.model,\n max_tokens: this.maxOutputTokens,\n temperature: 0.3,\n messages: [{ role: 'user', content: prompt }],\n }),\n signal,\n });\n } catch (networkError) {\n throw classifyClaudeServerError({\n cause: networkError,\n });\n }\n\n if (!response.ok) {\n const bodyText = await safeReadBody(response);\n throw classifyClaudeServerError({\n status: response.status,\n bodyText,\n headers: response.headers,\n cause: new Error(`Anthropic API error: ${response.status} - ${bodyText}`),\n });\n }\n\n let data: AnthropicMessagesResponse;\n try {\n data = (await response.json()) as AnthropicMessagesResponse;\n } catch (parseError) {\n throw new ServerClassifiedProviderError('Anthropic returned invalid JSON', {\n kind: 'parse_error',\n cause: parseError,\n });\n }\n\n if (data.error) {\n throw classifyClaudeServerError({\n status: response.status,\n bodyText: `${data.error.type ?? ''} ${data.error.message ?? ''}`,\n headers: response.headers,\n cause: new Error(`Anthropic API error: ${data.error.type} - ${data.error.message}`),\n });\n }\n\n const blocks = Array.isArray(data.content) ? data.content : [];\n const rawText = blocks\n .filter(block => block?.type === 'text' && typeof block.text === 'string')\n .map(block => block.text!)\n .join('\\n')\n .trim();\n\n if (!rawText) {\n logger.warn('SDK', 'Anthropic returned empty content array', {\n provider: 'claude',\n model: this.model,\n });\n }\n\n const usage = data.usage ?? {};\n const tokensUsed =\n typeof usage.input_tokens === 'number' || typeof usage.output_tokens === 'number'\n ? (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0)\n : undefined;\n\n return {\n rawText,\n ...(tokensUsed !== undefined ? { tokensUsed } : {}),\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n}\n\ninterface ClassifyInput {\n status?: number;\n bodyText?: string;\n headers?: Headers | { get(name: string): string | null };\n cause: unknown;\n}\n\n/**\n * Anthropic-specific HTTP error classification. Mirrors worker\n * `classifyClaudeError`, but extracted for server-beta and rebound to\n * Anthropic Messages REST semantics rather than SDK error classes.\n */\nexport function classifyClaudeServerError(input: ClassifyInput): ServerClassifiedProviderError {\n const status = input.status;\n const body = input.bodyText ?? '';\n const lower = body.toLowerCase();\n const retryAfterMs = input.headers ? parseRetryAfterMs(input.headers.get('retry-after')) : undefined;\n\n if (lower.includes('overloaded')) {\n return new ServerClassifiedProviderError(\n `Anthropic overloaded${status !== undefined ? ` (status ${status})` : ''}`,\n { kind: 'transient', cause: input.cause },\n );\n }\n\n if (status === 401 || status === 403 || lower.includes('invalid api key')) {\n return new ServerClassifiedProviderError(\n `Anthropic auth invalid${status !== undefined ? ` (status ${status})` : ''}`,\n { kind: 'auth_invalid', cause: input.cause },\n );\n }\n\n if (status === 429) {\n return new ServerClassifiedProviderError('Anthropic rate limit (429)', {\n kind: 'rate_limit',\n cause: input.cause,\n ...(retryAfterMs !== undefined ? { retryAfterMs } : {}),\n });\n }\n\n if (lower.includes('quota exceeded')) {\n return new ServerClassifiedProviderError('Anthropic quota exhausted', {\n kind: 'quota_exhausted',\n cause: input.cause,\n });\n }\n\n if (\n lower.includes('prompt is too long') ||\n lower.includes('context window') ||\n lower.includes('max_tokens')\n ) {\n return new ServerClassifiedProviderError('Anthropic context overflow', {\n kind: 'unrecoverable',\n cause: input.cause,\n });\n }\n\n if (status === 529) {\n return new ServerClassifiedProviderError('Anthropic overloaded (529)', {\n kind: 'transient',\n cause: input.cause,\n });\n }\n\n if (status !== undefined && status >= 500 && status < 600) {\n return new ServerClassifiedProviderError(`Anthropic upstream error (status ${status})`, {\n kind: 'transient',\n cause: input.cause,\n });\n }\n\n if (status === 400) {\n return new ServerClassifiedProviderError('Anthropic bad request (400)', {\n kind: 'unrecoverable',\n cause: input.cause,\n });\n }\n\n if (status === undefined) {\n const message = input.cause instanceof Error ? input.cause.message : String(input.cause);\n return new ServerClassifiedProviderError(`Anthropic network error: ${message}`, {\n kind: 'transient',\n cause: input.cause,\n });\n }\n\n return new ServerClassifiedProviderError(\n `Anthropic API error: ${status}${body ? ` - ${body.substring(0, 200)}` : ''}`,\n { kind: 'unrecoverable', cause: input.cause },\n );\n}\n\nasync function safeReadBody(response: Response): Promise<string> {\n try {\n return await response.text();\n } catch {\n return '';\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { logger } from '../../../utils/logger.js';\nimport {\n ServerClassifiedProviderError,\n classifyHttpProviderError,\n parseRetryAfterMs,\n} from './shared/error-classification.js';\nimport { buildServerGenerationPrompt } from './shared/prompt-builder.js';\nimport type {\n ServerGenerationContext,\n ServerGenerationProvider,\n ServerGenerationResult,\n} from './shared/types.js';\n\nconst GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1/models';\nconst DEFAULT_MODEL = 'gemini-2.5-flash';\n\nexport interface GeminiObservationProviderOptions {\n apiKey: string;\n model?: string;\n maxOutputTokens?: number;\n fetchImpl?: typeof fetch;\n}\n\ninterface GeminiResponse {\n candidates?: Array<{\n content?: { parts?: Array<{ text?: string }> };\n }>;\n usageMetadata?: { totalTokenCount?: number };\n error?: { code?: number; status?: string; message?: string };\n}\n\nexport class GeminiObservationProvider implements ServerGenerationProvider {\n readonly providerLabel = 'gemini' as const;\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxOutputTokens: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: GeminiObservationProviderOptions) {\n if (!options.apiKey) {\n throw new ServerClassifiedProviderError('Gemini API key not configured', {\n kind: 'auth_invalid',\n cause: new Error('apiKey is required'),\n });\n }\n this.apiKey = options.apiKey;\n this.model = options.model ?? DEFAULT_MODEL;\n this.maxOutputTokens = options.maxOutputTokens ?? 4096;\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async generate(\n context: ServerGenerationContext,\n signal?: AbortSignal,\n ): Promise<ServerGenerationResult> {\n const { prompt, skippedAll } = buildServerGenerationPrompt(context);\n if (skippedAll) {\n return {\n rawText: '<skip_summary reason=\"all_events_private\" />',\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n\n const url = `${GEMINI_API_URL}/${encodeURIComponent(this.model)}:generateContent?key=${encodeURIComponent(this.apiKey)}`;\n\n let response: Response;\n try {\n response = await this.fetchImpl(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n contents: [{ role: 'user', parts: [{ text: prompt }] }],\n generationConfig: {\n temperature: 0.3,\n maxOutputTokens: this.maxOutputTokens,\n },\n }),\n signal,\n });\n } catch (networkError) {\n throw classifyHttpProviderError({\n cause: networkError,\n providerLabel: 'Gemini',\n });\n }\n\n if (!response.ok) {\n const bodyText = await safeReadBody(response);\n throw classifyHttpProviderError({\n status: response.status,\n bodyText,\n headers: response.headers,\n cause: new Error(`Gemini API error: ${response.status} - ${bodyText}`),\n providerLabel: 'Gemini',\n });\n }\n\n let data: GeminiResponse;\n try {\n data = (await response.json()) as GeminiResponse;\n } catch (parseError) {\n throw new ServerClassifiedProviderError('Gemini returned invalid JSON', {\n kind: 'parse_error',\n cause: parseError,\n });\n }\n\n if (data.error) {\n throw classifyHttpProviderError({\n status: response.status,\n bodyText: `${data.error.status ?? ''} ${data.error.message ?? ''}`,\n headers: response.headers,\n cause: new Error(`Gemini API error: ${data.error.status} - ${data.error.message}`),\n providerLabel: 'Gemini',\n });\n }\n\n const rawText = data.candidates?.[0]?.content?.parts?.[0]?.text?.trim() ?? '';\n if (!rawText) {\n logger.warn('SDK', 'Gemini returned empty content', { provider: 'gemini', model: this.model });\n }\n\n const tokensUsed = typeof data.usageMetadata?.totalTokenCount === 'number'\n ? data.usageMetadata.totalTokenCount\n : undefined;\n\n return {\n rawText,\n ...(tokensUsed !== undefined ? { tokensUsed } : {}),\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n}\n\n// Re-export for tests/auditing parity with worker classifier surface.\nexport { parseRetryAfterMs };\n\nasync function safeReadBody(response: Response): Promise<string> {\n try {\n return await response.text();\n } catch {\n return '';\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Shared base-URL resolution for the OpenAI-compatible OpenRouter provider.\n *\n * Both the worker-runtime provider (src/services/worker/OpenRouterProvider.ts)\n * and the server-runtime provider\n * (src/server/generation/providers/OpenRouterObservationProvider.ts) use this\n * to turn the optional CLAUDE_MEM_OPENROUTER_BASE_URL setting into a concrete\n * `/chat/completions` endpoint. This makes the OpenRouter client a generic\n * OpenAI-compatible client.\n *\n * Closes #2382 (CLAUDE_MEM_OPENROUTER_BASE_URL), #2590 (custom provider with\n * configurable API base URL), #2622 (DeepSeek — OpenAI-compatible), and #2393\n * (LM Studio local model — OpenAI-compatible). Combined with the existing\n * CLAUDE_MEM_OPENROUTER_MODEL setting (passed verbatim), a user points the base\n * URL at any OpenAI-compatible endpoint and selects an arbitrary model id.\n *\n * Usage examples (set CLAUDE_MEM_PROVIDER=openrouter, then):\n *\n * DeepSeek:\n * CLAUDE_MEM_OPENROUTER_BASE_URL = https://api.deepseek.com\n * CLAUDE_MEM_OPENROUTER_MODEL = deepseek-chat\n * OPENROUTER_API_KEY (in ~/.claude-mem/.env) = <deepseek key>\n *\n * LM Studio (local, no key required):\n * CLAUDE_MEM_OPENROUTER_BASE_URL = http://localhost:1234/v1\n * CLAUDE_MEM_OPENROUTER_MODEL = <any local model id>\n *\n * Generic OpenAI-compatible endpoint:\n * CLAUDE_MEM_OPENROUTER_BASE_URL = https://my-gateway.example.com/v1\n * CLAUDE_MEM_OPENROUTER_MODEL = <model id>\n */\n\nexport const DEFAULT_OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';\n\nconst CHAT_COMPLETIONS_PATH = '/chat/completions';\n\n/**\n * Resolve the chat-completions endpoint from an optional configured base URL.\n *\n * Rules:\n * - unset/blank -> default OpenRouter chat-completions URL (behavior unchanged)\n * - a full URL already ending in `/chat/completions` -> used verbatim\n * - a base URL (e.g. `https://api.deepseek.com/v1`) -> `/chat/completions` appended\n * - trailing slashes are normalized before matching/appending\n */\nexport function resolveOpenRouterChatCompletionsUrl(baseUrl: string | undefined | null): string {\n const trimmed = (baseUrl ?? '').trim();\n if (!trimmed) {\n return DEFAULT_OPENROUTER_API_URL;\n }\n\n // Normalize trailing slashes so `.../v1/` and `.../v1` behave identically.\n const normalized = trimmed.replace(/\\/+$/, '');\n\n if (normalized.toLowerCase().endsWith(CHAT_COMPLETIONS_PATH)) {\n return normalized;\n }\n\n return `${normalized}${CHAT_COMPLETIONS_PATH}`;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { resolveOpenRouterChatCompletionsUrl } from '../../../shared/openrouter-base-url.js';\nimport { logger } from '../../../utils/logger.js';\nimport {\n ServerClassifiedProviderError,\n classifyHttpProviderError,\n} from './shared/error-classification.js';\nimport { buildServerGenerationPrompt } from './shared/prompt-builder.js';\nimport type {\n ServerGenerationContext,\n ServerGenerationProvider,\n ServerGenerationResult,\n} from './shared/types.js';\n\nconst DEFAULT_MODEL = 'anthropic/claude-3.5-sonnet';\n\nexport interface OpenRouterObservationProviderOptions {\n apiKey: string;\n model?: string;\n /**\n * Optional OpenAI-compatible base URL (#2382/#2590/#2622/#2393). When set,\n * requests POST to `<baseUrl>/chat/completions` (or verbatim if it already\n * ends in `/chat/completions`). When unset, the default OpenRouter endpoint\n * is used — behavior unchanged. Examples: https://api.deepseek.com (DeepSeek),\n * http://localhost:1234/v1 (LM Studio), a custom gateway base.\n */\n baseUrl?: string;\n maxOutputTokens?: number;\n siteUrl?: string;\n appName?: string;\n fetchImpl?: typeof fetch;\n}\n\ninterface OpenRouterResponse {\n choices?: Array<{ message?: { content?: string } }>;\n usage?: { total_tokens?: number };\n error?: { code?: string | number; message?: string };\n}\n\nexport class OpenRouterObservationProvider implements ServerGenerationProvider {\n readonly providerLabel = 'openrouter' as const;\n private readonly apiKey: string;\n private readonly model: string;\n private readonly apiUrl: string;\n private readonly maxOutputTokens: number;\n private readonly siteUrl: string;\n private readonly appName: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: OpenRouterObservationProviderOptions) {\n if (!options.apiKey) {\n throw new ServerClassifiedProviderError('OpenRouter API key not configured', {\n kind: 'auth_invalid',\n cause: new Error('apiKey is required'),\n });\n }\n this.apiKey = options.apiKey;\n // Model is passed verbatim so arbitrary OpenAI-compatible ids work. #2393.\n this.model = options.model ?? DEFAULT_MODEL;\n this.apiUrl = resolveOpenRouterChatCompletionsUrl(options.baseUrl);\n this.maxOutputTokens = options.maxOutputTokens ?? 4096;\n this.siteUrl = options.siteUrl ?? 'https://github.com/thedotmack/claude-mem';\n this.appName = options.appName ?? 'claude-mem';\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async generate(\n context: ServerGenerationContext,\n signal?: AbortSignal,\n ): Promise<ServerGenerationResult> {\n const { prompt, skippedAll } = buildServerGenerationPrompt(context);\n if (skippedAll) {\n return {\n rawText: '<skip_summary reason=\"all_events_private\" />',\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n\n let response: Response;\n try {\n response = await this.fetchImpl(this.apiUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'HTTP-Referer': this.siteUrl,\n 'X-Title': this.appName,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model: this.model,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0.3,\n max_tokens: this.maxOutputTokens,\n }),\n signal,\n });\n } catch (networkError) {\n throw classifyHttpProviderError({\n cause: networkError,\n providerLabel: 'OpenRouter',\n });\n }\n\n if (!response.ok) {\n const bodyText = await safeReadBody(response);\n throw classifyHttpProviderError({\n status: response.status,\n bodyText,\n headers: response.headers,\n cause: new Error(`OpenRouter API error: ${response.status} - ${bodyText}`),\n providerLabel: 'OpenRouter',\n });\n }\n\n let data: OpenRouterResponse;\n try {\n data = (await response.json()) as OpenRouterResponse;\n } catch (parseError) {\n throw new ServerClassifiedProviderError('OpenRouter returned invalid JSON', {\n kind: 'parse_error',\n cause: parseError,\n });\n }\n\n if (data.error) {\n throw classifyHttpProviderError({\n status: response.status,\n bodyText: `${data.error.code ?? ''} ${data.error.message ?? ''}`,\n headers: response.headers,\n cause: new Error(`OpenRouter API error: ${data.error.code} - ${data.error.message}`),\n providerLabel: 'OpenRouter',\n });\n }\n\n const rawText = data.choices?.[0]?.message?.content?.trim() ?? '';\n if (!rawText) {\n logger.warn('SDK', 'OpenRouter returned empty content', {\n provider: 'openrouter',\n model: this.model,\n });\n }\n\n const tokensUsed = typeof data.usage?.total_tokens === 'number' ? data.usage.total_tokens : undefined;\n\n return {\n rawText,\n ...(tokensUsed !== undefined ? { tokensUsed } : {}),\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n}\n\nasync function safeReadBody(response: Response): Promise<string> {\n try {\n return await response.text();\n } catch {\n return '';\n }\n}\n","\nimport { logger } from '../utils/logger.js';\nimport { ModeManager } from '../services/domain/ModeManager.js';\n\n// TODO(#2233): migrate to Anthropic tool-use API for deterministic JSON output. This text-XML path is the bridge.\n// Only strip fences when the entire payload is a single fenced block. Stripping\n// the first opening + last closing fence anywhere in the string can corrupt\n// content that contains internal fenced examples or surrounding prose\n// (CodeRabbit review on PR #2282).\nfunction stripCodeFences(text: string): string {\n const match = text.match(/^\\s*```(?:xml)?\\s*\\n([\\s\\S]*?)\\n```\\s*$/i);\n return match ? match[1] : text;\n}\n\nexport interface ParsedObservation {\n type: string;\n title: string | null;\n subtitle: string | null;\n facts: string[];\n narrative: string | null;\n concepts: string[];\n files_read: string[];\n files_modified: string[];\n}\n\nexport interface ParsedSummary {\n request: string | null;\n investigated: string | null;\n learned: string | null;\n completed: string | null;\n next_steps: string | null;\n notes: string | null;\n skipped?: boolean;\n skip_reason?: string | null;\n}\n\nexport type ParseResult =\n | { valid: true; observations: ParsedObservation[]; summary: ParsedSummary | null }\n | { valid: false };\n\nexport function parseAgentXml(raw: string, correlationId?: string | number): ParseResult {\n if (typeof raw !== 'string' || !raw.trim()) {\n return { valid: false };\n }\n\n raw = stripCodeFences(raw);\n\n const skipMatch = /<skip_summary(?:\\s+reason=\"([^\"]*)\")?\\s*\\/>/.exec(raw);\n if (skipMatch) {\n return {\n valid: true,\n observations: [],\n summary: {\n request: null,\n investigated: null,\n learned: null,\n completed: null,\n next_steps: null,\n notes: null,\n skipped: true,\n skip_reason: skipMatch[1] ?? null,\n },\n };\n }\n\n const firstRoot = /<(observation|summary)\\b/i.exec(raw);\n if (!firstRoot) {\n return { valid: false };\n }\n\n const rootName = firstRoot[1].toLowerCase();\n if (rootName === 'observation') {\n const observations = parseObservationBlocks(raw, correlationId);\n if (observations.length === 0) {\n return { valid: false };\n }\n return { valid: true, observations, summary: null };\n }\n\n const summary = parseSummaryBlock(raw, correlationId);\n if (!summary) {\n return { valid: false };\n }\n return { valid: true, observations: [], summary };\n}\n\nfunction parseObservationBlocks(text: string, correlationId?: string | number): ParsedObservation[] {\n const observations: ParsedObservation[] = [];\n\n const observationRegex = /<observation>([\\s\\S]*?)<\\/observation>/g;\n\n let match;\n while ((match = observationRegex.exec(text)) !== null) {\n const obsContent = match[1];\n\n const type = extractField(obsContent, 'type');\n const title = extractField(obsContent, 'title');\n const subtitle = extractField(obsContent, 'subtitle');\n const narrative = extractField(obsContent, 'narrative');\n const facts = extractArrayElements(obsContent, 'facts', 'fact');\n const concepts = extractArrayElements(obsContent, 'concepts', 'concept');\n const files_read = extractArrayElements(obsContent, 'files_read', 'file');\n const files_modified = extractArrayElements(obsContent, 'files_modified', 'file');\n\n const mode = ModeManager.getInstance().getActiveMode();\n const validTypes = mode.observation_types.map(t => t.id);\n const fallbackType = validTypes[0];\n let finalType = fallbackType;\n if (type) {\n if (validTypes.includes(type.trim())) {\n finalType = type.trim();\n } else {\n logger.error('PARSER', `Invalid observation type: ${type}, using \"${fallbackType}\"`, { correlationId });\n }\n } else {\n logger.error('PARSER', `Observation missing type field, using \"${fallbackType}\"`, { correlationId });\n }\n\n const cleanedConcepts = concepts.filter(c => c !== finalType);\n\n if (cleanedConcepts.length !== concepts.length) {\n logger.debug('PARSER', 'Removed observation type from concepts array', {\n correlationId,\n type: finalType,\n originalConcepts: concepts,\n cleanedConcepts\n });\n }\n\n if (!title && !narrative && facts.length === 0 && cleanedConcepts.length === 0) {\n logger.warn('PARSER', 'Skipping empty observation (all content fields null)', {\n correlationId,\n type: finalType\n });\n continue;\n }\n\n observations.push({\n type: finalType,\n title,\n subtitle,\n facts,\n narrative,\n concepts: cleanedConcepts,\n files_read,\n files_modified\n });\n }\n\n return observations;\n}\n\nfunction parseSummaryBlock(text: string, correlationId?: string | number): ParsedSummary | null {\n const summaryRegex = /<summary>([\\s\\S]*?)<\\/summary>/;\n const summaryMatch = summaryRegex.exec(text);\n if (!summaryMatch) return null;\n\n const summaryContent = summaryMatch[1];\n\n const request = extractField(summaryContent, 'request');\n const investigated = extractField(summaryContent, 'investigated');\n const learned = extractField(summaryContent, 'learned');\n const completed = extractField(summaryContent, 'completed');\n const next_steps = extractField(summaryContent, 'next_steps');\n const notes = extractField(summaryContent, 'notes'); \n\n if (!request && !investigated && !learned && !completed && !next_steps) {\n logger.warn('PARSER', 'Summary block has no sub-tags — rejecting false positive', { correlationId });\n return null;\n }\n\n return {\n request,\n investigated,\n learned,\n completed,\n next_steps,\n notes,\n };\n}\n\nfunction extractField(content: string, fieldName: string): string | null {\n const regex = new RegExp(`<${fieldName}>([\\\\s\\\\S]*?)</${fieldName}>`);\n const match = regex.exec(content);\n if (!match) return null;\n\n const trimmed = match[1].trim();\n return trimmed === '' ? null : trimmed;\n}\n\nfunction extractArrayElements(content: string, arrayName: string, elementName: string): string[] {\n const elements: string[] = [];\n\n const arrayRegex = new RegExp(`<${arrayName}>([\\\\s\\\\S]*?)</${arrayName}>`);\n const arrayMatch = arrayRegex.exec(content);\n\n if (!arrayMatch) {\n return elements;\n }\n\n const arrayContent = arrayMatch[1];\n\n const elementRegex = new RegExp(`<${elementName}>([\\\\s\\\\S]*?)</${elementName}>`, 'g');\n let elementMatch;\n while ((elementMatch = elementRegex.exec(arrayContent)) !== null) {\n const trimmed = elementMatch[1].trim();\n if (trimmed) {\n elements.push(trimmed);\n }\n }\n\n return elements;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { parseAgentXml, type ParsedObservation, type ParsedSummary } from '../../sdk/parser.js';\nimport { logger } from '../../utils/logger.js';\nimport {\n PostgresObservationRepository,\n PostgresObservationSourcesRepository,\n buildObservationGenerationKey,\n type PostgresObservation,\n} from '../../storage/postgres/observations.js';\nimport {\n PostgresObservationGenerationJobEventsRepository,\n PostgresObservationGenerationJobRepository,\n type PostgresObservationGenerationJob,\n} from '../../storage/postgres/generation-jobs.js';\nimport { PostgresAuthRepository } from '../../storage/postgres/auth.js';\nimport {\n withPostgresTransaction,\n type PostgresPool,\n} from '../../storage/postgres/pool.js';\nimport { stripTags } from '../../utils/tag-stripping.js';\n\n// processGeneratedResponse owns the full \"we got XML from a provider →\n// persist + link + advance outbox\" pipeline. Every side-effect runs inside\n// a single Postgres transaction so retries are idempotent:\n//\n// - observations.generation_key (UNIQUE per team/project) collapses retry\n// duplicates to a single row.\n// - observation_sources (UNIQUE on observation_id, source_type, source_id)\n// collapses duplicate source links.\n// - observation_generation_jobs.transitionStatus is the lifecycle gate.\n//\n// The function NEVER touches worker SessionStore tables, NEVER assumes a\n// Claude Code transcript shape, and ALWAYS reloads the job before mutating.\n// BullMQ payload data is advisory; the outbox row is canonical.\n\nexport type ProcessGeneratedResponseOutcome =\n | {\n kind: 'completed';\n jobId: string;\n observations: PostgresObservation[];\n privateContentDetected: boolean;\n }\n | { kind: 'parse_error'; jobId: string; reason: string };\n\nexport interface ProcessGeneratedResponseInput {\n pool: PostgresPool;\n job: PostgresObservationGenerationJob;\n rawText: string;\n modelId?: string;\n providerLabel: string;\n workerId?: string;\n // Phase 11 — identity context propagated from the BullMQ payload (and\n // ultimately the API-key that ingested the source row). Persisted on\n // observation_sources.metadata for traceability and re-emitted in the\n // observation.created audit row.\n apiKeyId?: string | null;\n actorId?: string | null;\n sourceAdapter?: string | null;\n}\n\nexport async function processGeneratedResponse(\n input: ProcessGeneratedResponseInput,\n): Promise<ProcessGeneratedResponseOutcome> {\n const { job, rawText } = input;\n\n const parsed = parseAgentXml(rawText, job.id);\n if (!parsed.valid) {\n return { kind: 'parse_error', jobId: job.id, reason: 'parser rejected response' };\n }\n\n // Skip-summary or zero-observation responses are still a success — the\n // provider explicitly decided there's nothing worth recording (e.g.\n // privacy-stripped batch). Mark the job completed with no observations.\n const observationsToWrite = parsed.observations ?? [];\n const skipped = parsed.summary?.skipped === true;\n const privateContentDetected = skipped || observationsToWrite.length === 0;\n\n return await withPostgresTransaction(input.pool, async (client) => {\n const obsRepo = new PostgresObservationRepository(client);\n const sourcesRepo = new PostgresObservationSourcesRepository(client);\n const jobsRepo = new PostgresObservationGenerationJobRepository(client);\n const eventsLogRepo = new PostgresObservationGenerationJobEventsRepository(client);\n const auditRepo = new PostgresAuthRepository(client);\n\n // Reload the job inside the transaction. If it was already completed\n // by another worker, return its existing observations idempotently.\n const fresh = await jobsRepo.getByIdForScope({\n id: job.id,\n projectId: job.projectId,\n teamId: job.teamId,\n });\n if (!fresh) {\n throw new Error(`generation job ${job.id} not found in scope`);\n }\n if (fresh.status === 'completed' || fresh.status === 'cancelled' || fresh.status === 'failed') {\n logger.info('SYSTEM', 'generation job already in terminal status; skipping persistence', {\n jobId: fresh.id,\n status: fresh.status,\n });\n return {\n kind: 'completed' as const,\n jobId: fresh.id,\n observations: [],\n privateContentDetected,\n };\n }\n\n const persisted: PostgresObservation[] = [];\n for (let index = 0; index < observationsToWrite.length; index++) {\n const parsedObservation = observationsToWrite[index]!;\n const content = renderObservationContent(parsedObservation);\n if (!content || content.trim().length === 0) {\n continue;\n }\n\n // Defense-in-depth: even if the parser slipped a private-tagged\n // string through, scrub before persisting.\n const scrubbed = stripTags(content);\n if (!scrubbed.stripped || scrubbed.stripped.trim().length === 0) {\n continue;\n }\n\n const generationKey = buildObservationGenerationKey({\n generationJobId: fresh.id,\n parsedObservationIndex: index,\n content: scrubbed.stripped,\n });\n\n const observation = await obsRepo.create({\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n serverSessionId: fresh.serverSessionId,\n kind: parsedObservation.type ?? 'observation',\n content: scrubbed.stripped,\n generationKey,\n metadata: {\n title: parsedObservation.title,\n subtitle: parsedObservation.subtitle,\n facts: parsedObservation.facts,\n narrative: parsedObservation.narrative,\n concepts: parsedObservation.concepts,\n files_read: parsedObservation.files_read,\n files_modified: parsedObservation.files_modified,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n },\n createdByJobId: fresh.id,\n });\n persisted.push(observation);\n\n await sourcesRepo.addSource({\n observationId: observation.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n sourceType: fresh.sourceType,\n sourceId: fresh.sourceId,\n agentEventId: fresh.agentEventId ?? null,\n generationJobId: fresh.id,\n metadata: {\n provider: input.providerLabel,\n parsedObservationIndex: index,\n // Phase 11 — denormalize identity context for traceability so an\n // operator can answer \"which api key produced this observation?\"\n // without joining back through generation_job → outbox → key.\n source_adapter: input.sourceAdapter ?? null,\n actor_id: input.actorId ?? null,\n api_key_id: input.apiKeyId ?? null,\n },\n });\n\n // Phase 11 — audit each generated observation. Using the SAME\n // generation_job_id reference so the audit chain (event_received →\n // generation_job.queued → generation_job.processing → observation.\n // created → observation.read) can be reconstructed.\n try {\n await auditRepo.createAuditLog({\n teamId: fresh.teamId,\n projectId: fresh.projectId,\n actorId: input.actorId ?? null,\n apiKeyId: input.apiKeyId ?? null,\n action: 'observation.created',\n resourceType: 'observation',\n resourceId: observation.id,\n details: {\n generationJobId: fresh.id,\n sourceType: fresh.sourceType,\n sourceId: fresh.sourceId,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n sourceAdapter: input.sourceAdapter ?? null,\n parsedObservationIndex: index,\n },\n });\n } catch (auditError) {\n logger.warn('SYSTEM', 'audit_log observation.created insert failed', {\n observationId: observation.id,\n error: auditError instanceof Error ? auditError.message : String(auditError),\n });\n }\n }\n\n // Advance outbox status. Phase 1 transitionStatus enforces legal\n // transitions and tenant scope inside its WHERE clause.\n await jobsRepo.transitionStatus({\n id: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n status: 'completed',\n });\n await eventsLogRepo.append({\n generationJobId: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n eventType: 'completed',\n statusAfter: 'completed',\n attempt: fresh.attempts,\n details: {\n provider: input.providerLabel,\n model: input.modelId ?? null,\n observationCount: persisted.length,\n privateContentDetected,\n workerId: input.workerId ?? null,\n },\n });\n\n // Audit log — best-effort; failure here would already be inside the\n // transaction so any insert error rolls everything back. We accept\n // that to keep the pipeline observable end-to-end.\n try {\n await auditRepo.createAuditLog({\n teamId: fresh.teamId,\n projectId: fresh.projectId,\n actorId: input.actorId ?? null,\n apiKeyId: input.apiKeyId ?? null,\n action: 'generation_job.completed',\n resourceType: 'observation_generation_job',\n resourceId: fresh.id,\n details: {\n generationJobId: fresh.id,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n observationCount: persisted.length,\n observationIds: persisted.map(o => o.id),\n sourceAdapter: input.sourceAdapter ?? null,\n },\n });\n } catch (auditError) {\n // The audit log table may not have a metadata column on older\n // schemas; swallow rather than failing generation.\n logger.warn('SYSTEM', 'audit log insert failed during generation', {\n jobId: fresh.id,\n error: auditError instanceof Error ? auditError.message : String(auditError),\n });\n }\n\n return {\n kind: 'completed' as const,\n jobId: fresh.id,\n observations: persisted,\n privateContentDetected,\n };\n });\n}\n\nexport interface MarkGenerationFailedInput {\n pool: PostgresPool;\n job: PostgresObservationGenerationJob;\n reason: string;\n classification?: string;\n retryable: boolean;\n workerId?: string;\n}\n\n/**\n * Move a generation job to a non-success terminal state. Used when the\n * provider returned an error or invalid XML. Retryable failures move the\n * job back to `queued` so reconciliation can re-enqueue; non-retryable\n * failures move to `failed`.\n */\nexport async function markGenerationFailed(input: MarkGenerationFailedInput): Promise<void> {\n await withPostgresTransaction(input.pool, async (client) => {\n const jobsRepo = new PostgresObservationGenerationJobRepository(client);\n const eventsLogRepo = new PostgresObservationGenerationJobEventsRepository(client);\n\n const fresh = await jobsRepo.getByIdForScope({\n id: input.job.id,\n projectId: input.job.projectId,\n teamId: input.job.teamId,\n });\n if (!fresh || fresh.status === 'completed' || fresh.status === 'cancelled') {\n return;\n }\n\n const canRetry = input.retryable && fresh.attempts < fresh.maxAttempts;\n const target = canRetry ? 'queued' : 'failed';\n\n await jobsRepo.transitionStatus({\n id: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n status: target,\n lastError: { reason: input.reason, classification: input.classification ?? null },\n ...(canRetry ? { nextAttemptAt: new Date(Date.now() + retryDelayMs(fresh.attempts)) } : {}),\n });\n\n await eventsLogRepo.append({\n generationJobId: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n eventType: canRetry ? 'retry_scheduled' : 'failed',\n statusAfter: target,\n attempt: fresh.attempts,\n details: {\n reason: input.reason,\n classification: input.classification ?? null,\n workerId: input.workerId ?? null,\n },\n });\n });\n}\n\n/**\n * Persist a parsed session summary as an observations row with kind='summary'.\n *\n * Wraps the same outbox transition / source-link / audit pipeline as\n * processGeneratedResponse but emits a single 'summary'-kind observation\n * derived from the summary fields. Idempotency is enforced through the same\n * `observations.generation_key` UNIQUE index — re-running the summary job\n * after a restart will collapse to one row.\n */\nexport async function processSessionSummaryResponse(\n input: ProcessGeneratedResponseInput,\n): Promise<ProcessGeneratedResponseOutcome> {\n const { job, rawText } = input;\n\n if (job.sourceType !== 'session_summary') {\n return { kind: 'parse_error', jobId: job.id, reason: 'session summary processor invoked on non-summary job' };\n }\n\n const parsed = parseAgentXml(rawText, job.id);\n if (!parsed.valid) {\n return { kind: 'parse_error', jobId: job.id, reason: 'parser rejected summary response' };\n }\n\n const summary = parsed.summary ?? null;\n const skipped = summary?.skipped === true;\n const summaryContent = summary ? renderSummaryContent(summary) : '';\n const privateContentDetected = skipped || summaryContent.trim().length === 0;\n\n return await withPostgresTransaction(input.pool, async (client) => {\n const obsRepo = new PostgresObservationRepository(client);\n const sourcesRepo = new PostgresObservationSourcesRepository(client);\n const jobsRepo = new PostgresObservationGenerationJobRepository(client);\n const eventsLogRepo = new PostgresObservationGenerationJobEventsRepository(client);\n const auditRepo = new PostgresAuthRepository(client);\n\n const fresh = await jobsRepo.getByIdForScope({\n id: job.id,\n projectId: job.projectId,\n teamId: job.teamId,\n });\n if (!fresh) {\n throw new Error(`session summary generation job ${job.id} not found in scope`);\n }\n if (fresh.status === 'completed' || fresh.status === 'cancelled' || fresh.status === 'failed') {\n logger.info('SYSTEM', 'session summary job already in terminal status; skipping persistence', {\n jobId: fresh.id,\n status: fresh.status,\n });\n return {\n kind: 'completed' as const,\n jobId: fresh.id,\n observations: [],\n privateContentDetected,\n };\n }\n\n const persisted: PostgresObservation[] = [];\n if (!privateContentDetected) {\n const scrubbed = stripTags(summaryContent);\n const scrubbedContent = scrubbed.stripped ?? '';\n if (scrubbedContent.trim().length > 0) {\n const generationKey = buildObservationGenerationKey({\n generationJobId: fresh.id,\n parsedObservationIndex: 0,\n content: scrubbedContent,\n });\n const observation = await obsRepo.create({\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n serverSessionId: fresh.serverSessionId,\n kind: 'summary',\n content: scrubbedContent,\n generationKey,\n metadata: {\n request: summary?.request ?? null,\n investigated: summary?.investigated ?? null,\n learned: summary?.learned ?? null,\n completed: summary?.completed ?? null,\n next_steps: summary?.next_steps ?? null,\n notes: summary?.notes ?? null,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n },\n createdByJobId: fresh.id,\n });\n persisted.push(observation);\n\n await sourcesRepo.addSource({\n observationId: observation.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n sourceType: 'session_summary',\n sourceId: fresh.sourceId,\n generationJobId: fresh.id,\n metadata: {\n provider: input.providerLabel,\n parsedObservationIndex: 0,\n source_adapter: input.sourceAdapter ?? null,\n actor_id: input.actorId ?? null,\n api_key_id: input.apiKeyId ?? null,\n },\n });\n\n // Phase 11 — observation.created audit for the summary observation.\n try {\n await auditRepo.createAuditLog({\n teamId: fresh.teamId,\n projectId: fresh.projectId,\n actorId: input.actorId ?? null,\n apiKeyId: input.apiKeyId ?? null,\n action: 'observation.created',\n resourceType: 'observation',\n resourceId: observation.id,\n details: {\n generationJobId: fresh.id,\n sourceType: 'session_summary',\n sourceId: fresh.sourceId,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n sourceAdapter: input.sourceAdapter ?? null,\n kind: 'summary',\n },\n });\n } catch (auditError) {\n logger.warn('SYSTEM', 'audit_log observation.created (summary) insert failed', {\n observationId: observation.id,\n error: auditError instanceof Error ? auditError.message : String(auditError),\n });\n }\n }\n }\n\n await jobsRepo.transitionStatus({\n id: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n status: 'completed',\n });\n await eventsLogRepo.append({\n generationJobId: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n eventType: 'completed',\n statusAfter: 'completed',\n attempt: fresh.attempts,\n details: {\n provider: input.providerLabel,\n model: input.modelId ?? null,\n observationCount: persisted.length,\n privateContentDetected,\n workerId: input.workerId ?? null,\n sourceType: 'session_summary',\n },\n });\n\n try {\n await auditRepo.createAuditLog({\n teamId: fresh.teamId,\n projectId: fresh.projectId,\n actorId: input.actorId ?? null,\n apiKeyId: input.apiKeyId ?? null,\n action: 'generation_job.completed',\n resourceType: 'observation_generation_job',\n resourceId: fresh.id,\n details: {\n generationJobId: fresh.id,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n observationCount: persisted.length,\n observationIds: persisted.map(o => o.id),\n sourceAdapter: input.sourceAdapter ?? null,\n sourceType: 'session_summary',\n },\n });\n } catch (auditError) {\n logger.warn('SYSTEM', 'audit log insert failed during summary generation', {\n jobId: fresh.id,\n error: auditError instanceof Error ? auditError.message : String(auditError),\n });\n }\n\n return {\n kind: 'completed' as const,\n jobId: fresh.id,\n observations: persisted,\n privateContentDetected,\n };\n });\n}\n\nfunction renderSummaryContent(summary: ParsedSummary): string {\n const parts: string[] = [];\n if (summary.request) parts.push(`Request: ${summary.request}`);\n if (summary.investigated) parts.push(`Investigated: ${summary.investigated}`);\n if (summary.learned) parts.push(`Learned: ${summary.learned}`);\n if (summary.completed) parts.push(`Completed: ${summary.completed}`);\n if (summary.next_steps) parts.push(`Next steps: ${summary.next_steps}`);\n if (summary.notes) parts.push(`Notes: ${summary.notes}`);\n return parts.join('\\n\\n').trim();\n}\n\nfunction renderObservationContent(observation: ParsedObservation): string {\n const parts: string[] = [];\n if (observation.title) parts.push(observation.title);\n if (observation.subtitle) parts.push(observation.subtitle);\n if (observation.narrative) parts.push(observation.narrative);\n if (observation.facts && observation.facts.length > 0) {\n parts.push(observation.facts.map(f => `- ${f}`).join('\\n'));\n }\n return parts.join('\\n\\n').trim();\n}\n\nfunction retryDelayMs(attempts: number): number {\n // Exponential backoff: 5s, 25s, 125s, capped at 10 minutes.\n const base = 5000 * Math.pow(5, Math.max(0, attempts));\n return Math.min(base, 10 * 60 * 1000);\n}\n"],"mappings":"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","names":["fs","path","os","execFile","promisify","path","existsSync","mkdirSync","readFileSync","join","LogLevel","existsSync","mkdirSync","join","readFileSync","readFileSync","existsSync","mkdirSync","join","dirname","homedir","join","homedir","existsSync","dirname","mkdirSync","readFileSync","existsSync","readFileSync","rmSync","existsSync","mkdirSync","readFileSync","writeFileSync","readFileSync","mkdirSync","existsSync","writeFileSync","existsSync","readFileSync","record","existsSync","readFileSync","PID_FILE","PID_FILE","existsSync","readFileSync","rmSync","execFileAsync","promisify","execFile","path","sep","readFileSync","writeFileSync","mkdirSync","existsSync","join","join","path","existsSync","readFileSync","mkdirSync","writeFileSync","createHash","createHash","readFileSync","existsSync","join","join","existsSync","readFileSync","DEFAULT_MODEL","safeReadBody","safeReadBody","DEFAULT_MODEL","safeReadBody"]}
1
+ {"version":3,"sources":["../src/sdk/index.ts","../src/storage/postgres/pool.ts","../src/storage/postgres/config.ts","../src/storage/postgres/schema.ts","../src/storage/postgres/utils.ts","../src/shared/platform-source.ts","../src/storage/postgres/agent-events.ts","../src/storage/postgres/auth.ts","../src/storage/postgres/generation-jobs.ts","../src/storage/postgres/observations.ts","../src/storage/postgres/projects.ts","../src/storage/postgres/server-sessions.ts","../src/storage/postgres/teams.ts","../src/storage/postgres/usage.ts","../src/storage/postgres/rate-limit.ts","../src/storage/postgres/index.ts","../src/services/sync/ChromaMcpManager.ts","../src/utils/logger.ts","../src/shared/paths.ts","../src/shared/hook-io.ts","../src/shared/SettingsDefaultsManager.ts","../src/shared/hook-constants.ts","../src/supervisor/env-sanitizer.ts","../src/supervisor/index.ts","../src/supervisor/process-registry.ts","../src/shared/spawn.ts","../src/supervisor/shutdown.ts","../src/supervisor/health-checker.ts","../src/shared/dependency-health.ts","../src/services/server/ErrorHandler.ts","../src/services/worker/search/errors.ts","../src/services/sync/ChromaSyncState.ts","../src/services/sync/ChromaSync.ts","../src/server/jobs/job-id.ts","../src/server/jobs/types.ts","../src/server/runtime/SessionGenerationPolicy.ts","../src/server/services/IngestEventsService.ts","../src/services/domain/ModeManager.ts","../src/server/generation/providers/shared/error-classification.ts","../src/utils/tag-stripping.ts","../src/server/generation/providers/shared/prompt-builder.ts","../src/server/generation/providers/ClaudeObservationProvider.ts","../src/server/generation/providers/GeminiObservationProvider.ts","../src/shared/openrouter-base-url.ts","../src/server/generation/providers/OpenRouterObservationProvider.ts","../src/sdk/parser.ts","../src/server/generation/processGeneratedResponse.ts"],"sourcesContent":["// cmem-sdk public API. Plan: plans/2026-05-25-cmem-sdk-and-server-rename.md §3-7.\n//\n// `createCmemClient(options)` builds the full in-process graph — pool →\n// schema bootstrap → repositories → tenancy resolution → Chroma (REQUIRED)\n// — and returns a CmemClient with every I/O method implemented: capture,\n// captureBatch, generate, captureAndGenerate, search, context,\n// startSession, endSession, and close.\n//\n// Existing internals in this directory (parser.ts, prompts.ts,\n// commit-verification.ts, hardened-options.ts, output-classifier.ts) are\n// reused internally (parser.ts + prompts.ts back generation). They are\n// intentionally NOT re-exported from the public surface here.\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\n\nimport {\n createPostgresPool,\n closePostgresPool,\n withPostgresTransaction,\n type PostgresPool,\n} from '../storage/postgres/pool.js';\nimport { parsePostgresConfig } from '../storage/postgres/config.js';\nimport { bootstrapServerPostgresSchema } from '../storage/postgres/schema.js';\nimport {\n createPostgresStorageRepositories,\n type PostgresStorageRepositories,\n} from '../storage/postgres/index.js';\nimport type { CreatePostgresAgentEventInput } from '../storage/postgres/agent-events.js';\nimport { ChromaSync, type ChromaDocument } from '../services/sync/ChromaSync.js';\nimport { ChromaMcpManager } from '../services/sync/ChromaMcpManager.js';\nimport { logger } from '../utils/logger.js';\nimport { IngestEventsService } from '../server/services/IngestEventsService.js';\nimport { ModeManager } from '../services/domain/ModeManager.js';\nimport {\n ClaudeObservationProvider,\n type ClaudeObservationProviderOptions,\n} from '../server/generation/providers/ClaudeObservationProvider.js';\nimport { GeminiObservationProvider } from '../server/generation/providers/GeminiObservationProvider.js';\nimport { OpenRouterObservationProvider } from '../server/generation/providers/OpenRouterObservationProvider.js';\nimport type {\n ServerGenerationProvider,\n ServerGenerationResult,\n} from '../server/generation/providers/shared/types.js';\nimport { processGeneratedResponse } from '../server/generation/processGeneratedResponse.js';\nimport type { PostgresObservation } from '../storage/postgres/observations.js';\n\n// ---------------------------------------------------------------------------\n// Public type re-exports. Plan: plans/2026-05-25-cmem-sdk-and-server-rename.md §7\n// line 263 — \"re-export `PostgresObservation`, the capture input type, search\n// result/context types, and the relevant `src/core/schemas` Zod types. Keep\n// the surface small and stable.\"\n//\n// Rules:\n// - Type-only (`export type`) for storage/core shapes — consumers should\n// read these as data, not instantiate Zod validators against them. This\n// also keeps the SDK bundle from gaining new runtime imports of\n// `src/core/schemas/*` (those modules pull `zod` at runtime).\n// - Only the entity types a Phase 8 SDK consumer would actually name\n// when they `import { ... } from 'claude-mem/sdk'` — implementation\n// helpers (`PostgresStorageRepositories` is already re-exported via the\n// `repos` field's structural type, internal `IngestEventsService`,\n// `*Row` interfaces, deterministic-key builders) stay private.\n// ---------------------------------------------------------------------------\n\n// Postgres storage entity types. `PostgresObservation` is the headline\n// re-export — every public result-bearing SDK method returns these. The\n// other three are the rest of the rows surfaced through `client.repos.*` so\n// consumers can name them without reaching into `claude-mem` internals.\nexport type {\n PostgresObservation,\n PostgresObservationSource,\n ObservationSourceType,\n} from '../storage/postgres/observations.js';\nexport type {\n PostgresAgentEvent,\n CreatePostgresAgentEventInput,\n} from '../storage/postgres/agent-events.js';\nexport type { PostgresServerSession } from '../storage/postgres/server-sessions.js';\nexport type { PostgresProject } from '../storage/postgres/projects.js';\nexport type { PostgresTeam, PostgresTeamMember, PostgresTeamRole } from '../storage/postgres/teams.js';\nexport type {\n PostgresObservationGenerationJob,\n PostgresObservationGenerationJobEvent,\n ObservationGenerationJobSourceType,\n ObservationGenerationJobStatus,\n ObservationGenerationJobEventType,\n} from '../storage/postgres/generation-jobs.js';\n// `PostgresStorageRepositories` is referenced on the `CmemClient.repos`\n// field, so consumers using e.g. `client.repos.agentEvents.getByIdForScope(...)`\n// can name the shape themselves. The implementation factory\n// (`createPostgresStorageRepositories`) stays private — consumers go through\n// `createCmemClient`, not through the repo factory directly.\nexport type { PostgresStorageRepositories } from '../storage/postgres/index.js';\n\n// Core schema types. These are the storage-agnostic shapes from\n// `src/core/schemas/`; they're a stable surface for downstream consumers\n// that want to model claude-mem data without importing the Postgres-specific\n// row types. Type-only — the matching `*Schema` Zod values stay internal\n// to keep the runtime surface frozen.\nexport type {\n MemoryItem,\n CreateMemoryItem,\n MemoryItemKind,\n MemorySource,\n CreateMemorySource,\n MemorySourceType,\n} from '../core/schemas/memory-item.js';\nexport type {\n AgentEvent,\n CreateAgentEvent,\n AgentEventSourceType,\n} from '../core/schemas/agent-event.js';\nexport type { ServerSession, CreateServerSession, ServerSessionStatus } from '../core/schemas/session.js';\nexport type { Project, CreateProject } from '../core/schemas/project.js';\nexport type { Team, CreateTeam, TeamMember, CreateTeamMember, TeamRole } from '../core/schemas/team.js';\n\n/**\n * Tuning options for the required Chroma semantic-search engine.\n *\n * Chroma is REQUIRED (see plan Executive Decision). There is no\n * `enabled: false` toggle — these options exist only to tune the\n * Chroma engine (collection naming, mcp subprocess path, etc.).\n */\nexport interface ChromaOptions {\n /**\n * Optional override for the Chroma collection-name prefix. Defaults to\n * the `cm__<projectId>` convention used by the existing ChromaSync.\n */\n collectionPrefix?: string;\n\n /**\n * Optional override for the chroma-mcp executable path. Defaults to\n * resolving `uvx chroma-mcp` on PATH.\n */\n mcpPath?: string;\n}\n\n/**\n * Pre-constructed generation provider accepted by\n * {@link CmemClientOptions.provider}. Identical shape to the server's\n * `ServerGenerationProvider`. Re-exported under a friendly name so\n * SDK consumers don't have to reach into `claude-mem/server`.\n */\nexport type CmemProvider = ServerGenerationProvider;\n\n/**\n * Configuration object accepted by {@link CmemClientOptions.provider}.\n * The SDK uses this to construct the matching concrete provider\n * ({@link ClaudeObservationProvider}, `GeminiObservationProvider`, or\n * `OpenRouterObservationProvider`). Defaults to `provider: 'claude'`.\n *\n * Mirrors the env-driven shape used by\n * `buildServerGenerationProviderFromEnv()` in\n * `src/server/runtime/create-server-service.ts:247`.\n */\nexport interface CmemProviderConfig {\n /** Provider API key. Required. */\n apiKey: string;\n /** Optional model id override (e.g. `claude-sonnet-4-6`). */\n model?: string;\n /** Provider kind. Defaults to `'claude'`. */\n provider?: 'claude' | 'gemini' | 'openrouter';\n /** OpenRouter-only: optional OpenAI-compatible base URL. */\n baseUrl?: string;\n}\n\n/**\n * Options accepted by {@link createCmemClient}.\n *\n * See plan §3 for the full graph this builds (pool → schema bootstrap →\n * repositories → ChromaSync). Provide either `databaseUrl` or `pool`;\n * provide explicit `teamId`+`projectId` in production (else the SDK\n * bootstraps + persists defaults to `$CLAUDE_MEM_DATA_DIR/sdk-tenant.json`).\n */\nexport interface CmemClientOptions {\n /**\n * Postgres connection URL. Falls back to `CLAUDE_MEM_SERVER_DATABASE_URL`\n * when neither this nor `pool` is supplied.\n */\n databaseUrl?: string;\n\n /**\n * Pre-built `pg.Pool` (or `PostgresQueryable`-compatible) instance.\n * When supplied, the SDK does NOT close it on `client.close()`.\n *\n * Typed as `unknown` here to keep the public type surface free of a\n * runtime dependency on `pg` types in consumer projects — Phase 3\n * narrows internally when consumed.\n */\n pool?: unknown;\n\n /**\n * Tenant team identifier (UUID). When omitted, the SDK calls\n * `ensureDefaults()` and persists the resolved ID.\n */\n teamId?: string;\n\n /**\n * Tenant project identifier (UUID). When omitted, the SDK calls\n * `ensureDefaults()` and persists the resolved ID.\n */\n projectId?: string;\n\n /**\n * Optional human-readable team name used only when the SDK has to\n * create a default team.\n */\n teamName?: string;\n\n /**\n * Optional human-readable project name used only when the SDK has to\n * create a default project (i.e. neither `projectId` nor a persisted\n * tenant file is present).\n */\n projectName?: string;\n\n /**\n * Generation provider. Three accepted shapes:\n * 1. A pre-constructed {@link CmemProvider} (anything with the right\n * `.generate(context, signal?)` shape — typically a server-internal\n * provider class like {@link ClaudeObservationProvider}).\n * 2. A {@link CmemProviderConfig} `{ apiKey, model?, provider? }` —\n * the SDK instantiates the matching concrete provider for you.\n * 3. `undefined` — the SDK falls back to the env-driven resolution\n * mirroring `buildServerGenerationProviderFromEnv()` in\n * `create-server-service.ts:247`. Reading `CLAUDE_MEM_SERVER_PROVIDER`\n * + `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` / `OPENROUTER_API_KEY`\n * and `CLAUDE_MEM_SERVER_MODEL`. Defaults to Claude when\n * `ANTHROPIC_API_KEY` is set.\n *\n * When `client.generate(...)` is called without any of these resolving\n * to a usable provider, the call rejects with a clear error.\n */\n provider?: CmemProvider | CmemProviderConfig;\n\n /**\n * Chroma tuning. Chroma is REQUIRED; this object does NOT include an\n * `enabled` flag. See {@link ChromaOptions}.\n */\n chroma?: ChromaOptions;\n}\n\n/**\n * Response envelope for {@link CmemClient.search}.\n *\n * `observations` are hydrated {@link PostgresObservation} rows — the same\n * shape callers see when reading directly from `repos.observations.*`.\n *\n * `chroma: true` means the result came from the Chroma semantic engine\n * (the default and intended path). `chroma: false` + `degraded: true`\n * means Chroma failed at request time and the SDK fell through to the\n * Postgres FTS safety net. This is a RUNTIME state, not a config toggle;\n * a `logger.error('CHROMA', …)` is emitted whenever it happens so an\n * operator can investigate the chroma-mcp subprocess.\n *\n * `error` is only present on the degraded branch. See plan §6.\n */\nexport interface CmemSearchResponse {\n observations: PostgresObservation[];\n chroma: boolean;\n degraded: boolean;\n error?: { message: string };\n}\n\n/**\n * Response envelope for {@link CmemClient.context}. Mirrors the\n * `ServerV1PostgresRoutes` `/context` shape — observations + a single\n * `\\n\\n`-joined string. `degraded` propagates from the underlying\n * {@link CmemSearchResponse}.\n */\nexport interface CmemContextResponse {\n observations: PostgresObservation[];\n context: string;\n degraded: boolean;\n}\n\n/**\n * The friendly capture-event shape accepted by\n * {@link CmemClient.capture} / {@link CmemClient.captureBatch}. Phase 4\n * maps this to `CreatePostgresAgentEventInput`. `projectId` + `teamId`\n * are added by the SDK from the resolved tenancy, not by the caller.\n */\nexport interface CmemCaptureEvent {\n /** Source system label (e.g. 'custom-cli', 'my-bot'). Required. */\n sourceAdapter: string;\n /** Event type tag (e.g. 'message', 'tool-use'). Required. */\n eventType: string;\n /** Free-form payload. Will be JSON-serialized. */\n payload: Record<string, unknown>;\n /** When this event happened. Defaults to `new Date()`. */\n occurredAt?: Date | string | number;\n /** Caller's idempotency key for dedup. Optional but recommended. */\n sourceEventId?: string;\n /** Optional server-session id this event belongs to. */\n serverSessionId?: string;\n /** Optional platform-source label (e.g. 'claude-code', 'opencode'). */\n platformSource?: string;\n /** Free-form metadata. Defaults to `{}`. */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Result of a {@link CmemClient.capture} / `captureBatch` call. Mirrors\n * the underlying `IngestEventsService.ingestOne` return shape, projected\n * down to the IDs consumers actually want.\n *\n * `generationJobId` is the queued `observation_generation_jobs` row that\n * Phase 5's `generate(jobId)` will consume. The SDK never enqueues to\n * Redis/BullMQ, so the job stays in status `queued` until you run it.\n */\nexport interface CmemCaptureResult {\n agentEventId: string;\n generationJobId: string;\n}\n\n/**\n * Optional input accepted by {@link CmemClient.startSession}. Maps to\n * `PostgresServerSessionsRepository.create`'s tenant-scoped input.\n */\nexport interface CmemStartSessionInput {\n /** Caller-supplied external session id (used for idempotent dedup). */\n externalSessionId?: string;\n /** Caller-supplied content session id (used for idempotent dedup). */\n contentSessionId?: string;\n /** Agent identifier (e.g. user or assistant id). */\n agentId?: string;\n /** Agent type label. */\n agentType?: string;\n /** Platform-source label (e.g. 'claude-code', 'opencode'). */\n platformSource?: string;\n /** Free-form metadata. */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Identifier returned by {@link CmemClient.startSession}.\n */\nexport interface CmemSessionInfo {\n serverSessionId: string;\n}\n\n/**\n * Result of a {@link CmemClient.generate} call. Mirrors\n * `processGeneratedResponse`'s `'completed'` outcome — the persisted\n * observations and the provider/model that produced them.\n *\n * `observations: []` is a normal-successful outcome when the provider\n * decided there was nothing worth recording (privacy-skipped batch,\n * `<skip_summary />` response, etc.). The job is marked completed\n * either way.\n */\nexport interface CmemGenerateResult {\n jobId: string;\n observations: PostgresObservation[];\n providerLabel: string;\n modelId?: string;\n /** `true` when the response was a privacy/skip signal with no observations. */\n privateContentDetected: boolean;\n}\n\n/**\n * Result of {@link CmemClient.captureAndGenerate}. The IDs from the\n * underlying {@link CmemCaptureResult} plus the persisted\n * {@link CmemGenerateResult}.\n */\nexport interface CmemCaptureAndGenerateResult {\n agentEventId: string;\n generationJobId: string;\n result: CmemGenerateResult;\n}\n\n/**\n * Public client returned by {@link createCmemClient}. All methods are\n * implemented: construction/tenancy/`close()` plus the capture, generate,\n * search, context, and session I/O paths.\n */\nexport interface CmemClient {\n /** Resolved tenant team ID (UUID). */\n readonly teamId: string;\n /** Resolved tenant project ID (UUID). */\n readonly projectId: string;\n /** Repository facade over the Postgres storage layer. */\n readonly repos: PostgresStorageRepositories;\n /** The underlying `pg.Pool` instance the SDK is using. */\n readonly pool: PostgresPool;\n /** The constructed `ChromaSync` for semantic search (Phase 6 consumes). */\n readonly chromaSync: ChromaSync;\n /** Persist a single agent event + outbox generation job. */\n capture(event: CmemCaptureEvent): Promise<CmemCaptureResult>;\n /** Persist many events in a single transactional batch. */\n captureBatch(events: CmemCaptureEvent[]): Promise<CmemCaptureResult[]>;\n /**\n * Run the in-process generation pipeline for a queued job. Transitions\n * the job `queued → processing`, calls the configured provider, then\n * delegates to `processGeneratedResponse` which writes the observations\n * and marks the job `completed` in a single Postgres transaction.\n */\n generate(jobId: string): Promise<CmemGenerateResult>;\n /** Sugar for capture-then-generate. */\n captureAndGenerate(event: CmemCaptureEvent): Promise<CmemCaptureAndGenerateResult>;\n /** Semantic search with FTS runtime safety net. See plan §6. */\n search(input: { query: string; limit?: number }): Promise<CmemSearchResponse>;\n /** Search + join contents into a single `\\n\\n`-delimited context blob. */\n context(input: { query: string; limit?: number }): Promise<CmemContextResponse>;\n /** Begin a server session for grouping subsequent captures. */\n startSession(input?: CmemStartSessionInput): Promise<CmemSessionInfo>;\n /** End the named server session. Idempotent. */\n endSession(serverSessionId: string): Promise<void>;\n /** Close Chroma + pool (if SDK-owned). Safe to call repeatedly. */\n close(): Promise<void>;\n}\n\n/**\n * Resolve the claude-mem data directory without pulling in\n * `src/shared/paths.ts` (which would transitively reference the\n * worker's settings/logger layer). Mirrors the priority that\n * `paths.ts::resolveDataDir` follows for the SDK's narrow needs:\n * 1. `CLAUDE_MEM_DATA_DIR` env var\n * 2. `$HOME/.claude-mem`\n *\n * Kept inline so the SDK bundle stays free of the worker's runtime\n * surface. See plan §3, anti-pattern guards.\n */\nfunction resolveSdkDataDir(): string {\n const fromEnv = process.env.CLAUDE_MEM_DATA_DIR;\n if (fromEnv && fromEnv.length > 0) {\n return fromEnv;\n }\n return path.join(os.homedir(), '.claude-mem');\n}\n\ninterface ResolvedTenancy {\n teamId: string;\n projectId: string;\n}\n\n/**\n * Type guard that recognizes an already-constructed\n * {@link ServerGenerationProvider}. We don't `instanceof`-check the concrete\n * provider classes because consumers may pass a custom implementation —\n * the structural check (`.generate` + `.providerLabel`) is sufficient.\n */\nfunction isCmemProvider(value: unknown): value is ServerGenerationProvider {\n if (value === null || typeof value !== 'object') return false;\n const candidate = value as Record<string, unknown>;\n return (\n typeof candidate.providerLabel === 'string'\n && typeof candidate.generate === 'function'\n );\n}\n\n/**\n * Instantiate the concrete {@link ServerGenerationProvider} for a resolved\n * `{ apiKey, model?, provider?, baseUrl? }`. Shared by the config-object and\n * env-driven resolution paths. Mirrors the dispatch in\n * `src/server/runtime/create-server-service.ts:261-279`.\n */\nfunction instantiateProvider(opts: {\n apiKey: string;\n model?: string;\n provider?: string;\n baseUrl?: string;\n}): ServerGenerationProvider {\n const kind = (opts.provider ?? 'claude').toLowerCase();\n if (kind === 'claude' || kind === 'anthropic') {\n const o: ClaudeObservationProviderOptions = { apiKey: opts.apiKey };\n if (opts.model) o.model = opts.model;\n return new ClaudeObservationProvider(o);\n }\n if (kind === 'gemini') {\n const o: { apiKey: string; model?: string } = { apiKey: opts.apiKey };\n if (opts.model) o.model = opts.model;\n return new GeminiObservationProvider(o);\n }\n if (kind === 'openrouter') {\n const o: { apiKey: string; model?: string; baseUrl?: string } = { apiKey: opts.apiKey };\n if (opts.model) o.model = opts.model;\n if (opts.baseUrl) o.baseUrl = opts.baseUrl;\n return new OpenRouterObservationProvider(o);\n }\n throw new Error(\n `cmem-sdk: unsupported provider kind \"${opts.provider ?? 'claude'}\". ` +\n `Expected one of: \"claude\", \"gemini\", \"openrouter\".`\n );\n}\n\n/**\n * Env-driven provider resolution. Mirrors\n * `buildServerGenerationProviderFromEnv()` in\n * `src/server/runtime/create-server-service.ts:261-279`. Returns `null`\n * when no provider can be resolved from the environment — callers surface\n * a clear error at `generate()` time instead of failing at construction.\n */\nfunction buildProviderFromEnv(): ServerGenerationProvider | null {\n const explicit = (process.env.CLAUDE_MEM_SERVER_PROVIDER ?? '').trim().toLowerCase();\n const model = process.env.CLAUDE_MEM_SERVER_MODEL;\n try {\n if (explicit === 'claude' || explicit === 'anthropic' || explicit === '') {\n const apiKey =\n process.env.ANTHROPIC_API_KEY ?? process.env.CLAUDE_MEM_ANTHROPIC_API_KEY ?? '';\n if (apiKey) return instantiateProvider({ apiKey, model, provider: 'claude' });\n // No claude key. If claude was explicitly demanded, give up; if the\n // provider was unset (''), fall through to the gemini/openrouter scan so\n // an installer that only set GEMINI_API_KEY still works.\n if (explicit !== '') return null;\n }\n if (explicit === 'gemini' || (explicit === '' && process.env.GEMINI_API_KEY)) {\n const apiKey = process.env.GEMINI_API_KEY ?? process.env.CLAUDE_MEM_GEMINI_API_KEY ?? '';\n if (!apiKey) return null;\n return instantiateProvider({ apiKey, model, provider: 'gemini' });\n }\n if (explicit === 'openrouter' || (explicit === '' && process.env.OPENROUTER_API_KEY)) {\n const apiKey =\n process.env.OPENROUTER_API_KEY ?? process.env.CLAUDE_MEM_OPENROUTER_API_KEY ?? '';\n if (!apiKey) return null;\n const baseUrl =\n process.env.CLAUDE_MEM_OPENROUTER_BASE_URL ?? process.env.OPENROUTER_BASE_URL;\n return instantiateProvider({ apiKey, model, provider: 'openrouter', baseUrl });\n }\n } catch {\n return null;\n }\n return null;\n}\n\n/**\n * Resolve a {@link ServerGenerationProvider} from {@link CmemClientOptions.provider}.\n * Three branches, in order:\n * 1. Already-constructed provider → return as-is.\n * 2. {@link CmemProviderConfig} → instantiate via {@link instantiateProvider}.\n * 3. `undefined` → fall back to {@link buildProviderFromEnv}.\n * Returns `null` only when env resolution found nothing — `generate()` then\n * surfaces a clear \"no provider configured\" error.\n */\nfunction resolveProvider(\n optionsProvider: CmemClientOptions['provider']\n): ServerGenerationProvider | null {\n if (optionsProvider === undefined) {\n return buildProviderFromEnv();\n }\n if (isCmemProvider(optionsProvider)) {\n return optionsProvider;\n }\n // Must be CmemProviderConfig.\n return instantiateProvider(optionsProvider);\n}\n\n/**\n * Eager mode initialization. `parseAgentXml` (`src/sdk/parser.ts:105`)\n * calls `ModeManager.getInstance().getActiveMode()` with no fallback —\n * if no mode is loaded, it throws. We mirror `loadServerMode()` in\n * `src/server/runtime/create-server-service.ts:167-177` and load `'code'`\n * once at SDK construction so the failure (a missing/broken\n * `plugin/modes/code.json`) surfaces during `createCmemClient(...)`\n * instead of at every `generate()` call.\n *\n * Throws a clear cmem-sdk-tagged error if the mode cannot be loaded.\n */\nfunction initializeMode(): void {\n try {\n const mgr = ModeManager.getInstance();\n mgr.loadMode('code');\n mgr.getActiveMode();\n } catch (err) {\n const underlying = err instanceof Error ? err.message : String(err);\n throw new Error(\n 'cmem-sdk: failed to load default observation mode \"code\". ' +\n 'Generation requires a mode (parser at src/sdk/parser.ts:105 has no fallback). ' +\n 'Verify the bundled plugin/modes/code.json is present. ' +\n 'Underlying: ' +\n underlying\n );\n }\n}\n\n/**\n * Read-or-create the default `{teamId, projectId}` pair. Per plan §3\n * line 191, this is the headless equivalent of the server runtime's\n * implicit tenancy — it persists IDs to a state file under\n * `$CLAUDE_MEM_DATA_DIR/sdk-tenant.json` so subsequent SDK runs reuse\n * them without re-creating rows.\n *\n * Production consumers are expected to pass explicit `teamId`+`projectId`\n * via {@link CmemClientOptions} and bypass this path entirely.\n */\nasync function resolveTenancy(\n options: CmemClientOptions,\n pool: PostgresPool\n): Promise<ResolvedTenancy> {\n if (options.teamId && options.projectId) {\n return { teamId: options.teamId, projectId: options.projectId };\n }\n\n const stateFile = path.join(resolveSdkDataDir(), 'sdk-tenant.json');\n\n // 1. Try to reuse a previously persisted pair.\n try {\n const raw = fs.readFileSync(stateFile, 'utf8');\n const parsed = JSON.parse(raw) as { teamId?: unknown; projectId?: unknown };\n if (typeof parsed.teamId === 'string' && typeof parsed.projectId === 'string') {\n return { teamId: parsed.teamId, projectId: parsed.projectId };\n }\n } catch {\n // Missing or corrupt — fall through to create.\n }\n\n // 2. Create default team + project in one transaction so a half-write\n // can't leave an orphan team.\n const created = await withPostgresTransaction(pool, async (tx) => {\n const txRepos = createPostgresStorageRepositories(tx);\n const team = await txRepos.teams.create({ name: options.teamName ?? 'default' });\n const project = await txRepos.projects.create({\n teamId: team.id,\n name: options.projectName ?? 'default',\n });\n return { teamId: team.id, projectId: project.id };\n });\n\n // 3. Best-effort persist; failure to write the state file is not fatal —\n // it just means the next run will re-create the pair. Most callers\n // will pass explicit IDs in production anyway.\n try {\n fs.mkdirSync(path.dirname(stateFile), { recursive: true });\n fs.writeFileSync(stateFile, JSON.stringify(created, null, 2));\n } catch {\n // ignore\n }\n\n return created;\n}\n\n/**\n * Copy each `keys` entry from `source` to `target` only when it is defined.\n * Keeps the optional-field plumbing on `mapCaptureEvent` / `startSession`\n * one line each under `exactOptionalPropertyTypes`.\n */\nfunction assignDefined(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n keys: readonly string[],\n): void {\n for (const key of keys) {\n if (source[key] !== undefined) target[key] = source[key];\n }\n}\n\n/**\n * Build {@link ChromaDocument}s for the freshly-persisted observations and\n * index them into the per-tenant Chroma collection via the now-public\n * `ChromaSync.addDocuments` (which batches + delete-then-add reconciles on\n * the `already exist` race). The document `id` is the Postgres observation\n * UUID — one Chroma doc per observation, NOT the SQLite-shaped\n * `obs_<sqlite_id>_<field>` split that `syncObservation` produces.\n *\n * Indexing failure is degraded, not catastrophic: the observations are\n * already in Postgres. The next `client.search()` either hits Chroma on\n * the retry-batched delete+add path, or falls through to FTS via the\n * search-side safety net. We log a warning and continue. See plan §6\n * line 244-247.\n */\nasync function indexObservationsToChroma(\n chromaSync: ChromaSync,\n observations: PostgresObservation[],\n scope: { projectId: string; teamId: string },\n): Promise<void> {\n if (observations.length === 0) return;\n const docs: ChromaDocument[] = observations.map(observation => {\n const metadata: Record<string, string | number> = {\n projectId: scope.projectId,\n teamId: scope.teamId,\n kind: observation.kind,\n observationId: observation.id,\n observationType: observation.kind,\n // ChromaSync's clean step filters out empty strings (ChromaSync.ts:291-295),\n // so passing '' for missing server_session_id collapses cleanly to\n // metadata-absent rather than indexing a meaningless empty value.\n serverSessionId: observation.serverSessionId ?? '',\n createdAt: new Date(observation.createdAtEpoch).toISOString(),\n };\n return {\n id: observation.id,\n document: observation.content,\n metadata,\n };\n });\n try {\n await chromaSync.addDocuments(docs);\n } catch (err) {\n // addDocuments swallows per-batch errors internally and returns a\n // count; an outer throw means something more fundamental (ensure\n // collection / connect) failed. Log + continue: the observations are\n // still persisted in Postgres, search will degrade to FTS until the\n // operator restarts chroma-mcp.\n logger.error(\n 'CHROMA',\n 'observation indexing failed after generate(); observations are persisted but unsearchable in Chroma until reindex',\n { projectId: scope.projectId, teamId: scope.teamId, observationCount: observations.length },\n err as Error,\n );\n }\n}\n\n/**\n * Construct a {@link CmemClient}.\n *\n * Phase 3 wires:\n * 1. Pool (either supplied or built from `databaseUrl` /\n * `CLAUDE_MEM_SERVER_DATABASE_URL`).\n * 2. Idempotent schema bootstrap.\n * 3. Repository facade.\n * 4. Tenancy resolution (`teamId`+`projectId`).\n * 5. Chroma (REQUIRED) — `ChromaSync(projectId).ensureCollectionExists()`.\n * If `uvx chroma-mcp` cannot start, `createCmemClient` REJECTS and\n * cleans up the SDK-owned pool before throwing.\n *\n * The returned client's I/O methods (capture, generate, search, context,\n * sessions) are all implemented below.\n */\nexport async function createCmemClient(options: CmemClientOptions): Promise<CmemClient> {\n // 0. Mode initialization. `parseAgentXml` requires an active mode\n // (src/sdk/parser.ts:105 has no fallback). Loading at construction\n // surfaces missing-mode-file errors here instead of at every\n // generate() call. Consistent with the Chroma-required philosophy:\n // fail fast at construction, not silently at runtime.\n initializeMode();\n\n // 1. Pool — either consumer-supplied or SDK-owned.\n let pool: PostgresPool;\n let ownsPool = false;\n if (options.pool) {\n pool = options.pool as PostgresPool;\n } else {\n // Allow `databaseUrl` to short-circuit env lookup by temporarily\n // overlaying it onto process.env for `parsePostgresConfig`.\n const envOverlay: NodeJS.ProcessEnv = options.databaseUrl\n ? { ...process.env, CLAUDE_MEM_SERVER_DATABASE_URL: options.databaseUrl }\n : process.env;\n const cfg = parsePostgresConfig({ env: envOverlay });\n if (!cfg) {\n throw new Error(\n 'cmem-sdk: CLAUDE_MEM_SERVER_DATABASE_URL or options.databaseUrl is required'\n );\n }\n pool = createPostgresPool(cfg);\n ownsPool = true;\n }\n\n // 2. Idempotent schema bootstrap.\n try {\n await bootstrapServerPostgresSchema(pool);\n } catch (err) {\n if (ownsPool) {\n await closePostgresPool(pool).catch(() => {});\n }\n throw err;\n }\n\n // 3. Repository facade.\n const repos = createPostgresStorageRepositories(pool);\n\n // 4. Tenancy resolution.\n let teamId: string;\n let projectId: string;\n try {\n const tenancy = await resolveTenancy(options, pool);\n teamId = tenancy.teamId;\n projectId = tenancy.projectId;\n } catch (err) {\n if (ownsPool) {\n await closePostgresPool(pool).catch(() => {});\n }\n throw err;\n }\n\n // 5. Chroma — REQUIRED. ensureCollectionExists boots the chroma-mcp\n // subprocess (via ChromaMcpManager) and creates the per-project\n // collection. It rethrows any error other than \"already exists\", so a\n // subprocess that can't start surfaces here and rejects construction.\n const chromaSync = new ChromaSync(projectId);\n try {\n await chromaSync.ensureCollectionExists();\n } catch (err) {\n // Clean up everything the SDK owns before rejecting.\n await chromaSync.close().catch(() => {});\n if (ownsPool) {\n await closePostgresPool(pool).catch(() => {});\n }\n const underlying = err instanceof Error ? err.message : String(err);\n throw new Error(\n 'cmem-sdk: Chroma is required but uvx chroma-mcp could not start. ' +\n 'Install uv (https://docs.astral.sh/uv/) and ensure chroma-mcp is available. ' +\n 'Underlying: ' +\n underlying\n );\n }\n\n // 6. Build the IngestEventsService once at construction. `resolveEventQueue`\n // MUST return `null` so the outbox row is written but no BullMQ enqueue\n // is attempted. Generation never runs inline here — that is Phase 5.\n // See IngestEventsService.ts:235-238 for the queued_only short-circuit.\n const ingest = new IngestEventsService({\n pool,\n resolveEventQueue: () => null,\n });\n\n // 7. Resolve the generation provider (Phase 5). Best-effort at construction\n // so the SDK can still serve capture/search even without a provider; a\n // `client.generate(...)` call without a resolved provider rejects with a\n // clear error. Provider exceptions during resolution (e.g. a\n // `ClaudeObservationProvider` constructor rejecting because apiKey is\n // empty) bubble up here and abort construction.\n const provider = resolveProvider(options.provider);\n\n // 8. Build and return the client. All I/O methods (capture, captureBatch,\n // generate, captureAndGenerate, search, context, startSession,\n // endSession, close) are implemented below.\n let closed = false;\n\n function mapCaptureEvent(event: CmemCaptureEvent): CreatePostgresAgentEventInput {\n const input: CreatePostgresAgentEventInput = {\n projectId,\n teamId,\n sourceAdapter: event.sourceAdapter,\n eventType: event.eventType,\n payload: event.payload as CreatePostgresAgentEventInput['payload'],\n occurredAt: event.occurredAt ?? new Date(),\n };\n assignDefined(input as unknown as Record<string, unknown>, event as unknown as Record<string, unknown>, [\n 'sourceEventId',\n 'serverSessionId',\n 'platformSource',\n 'metadata',\n ]);\n return input;\n }\n\n const client: CmemClient = {\n teamId,\n projectId,\n repos,\n pool,\n chromaSync,\n async capture(event: CmemCaptureEvent): Promise<CmemCaptureResult> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n const result = await ingest.ingestOne(mapCaptureEvent(event), {\n source: 'cmem-sdk.capture',\n sourceAdapter: event.sourceAdapter,\n });\n // resolveEventQueue: () => null guarantees `outbox` is non-null and\n // `enqueueState` is 'queued_only' whenever the default `generate: true`\n // path runs (IngestEventsService.ts:236-238). If a future caller\n // disables generation we surface a clear error rather than a TS-cast lie.\n if (!result.outbox) {\n throw new Error(\n 'cmem-sdk: capture expected a queued outbox row but received none. ' +\n 'This indicates IngestEventsService was invoked with generate: false; ' +\n 'capture() must produce a generation job for Phase 5 consumption.'\n );\n }\n return {\n agentEventId: result.event.id,\n generationJobId: result.outbox.id,\n };\n },\n async captureBatch(events: CmemCaptureEvent[]): Promise<CmemCaptureResult[]> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n if (events.length === 0) {\n return [];\n }\n // ingestBatch wraps all writes in a single Postgres transaction\n // (IngestEventsService.ts:170-220), which is materially better than\n // looping ingestOne per event. Same resolveEventQueue: () => null\n // semantics apply to every row.\n const results = await ingest.ingestBatch(\n events.map(mapCaptureEvent),\n { source: 'cmem-sdk.captureBatch' }\n );\n return results.map((r, i) => {\n if (!r.outbox) {\n const sourceEventId = events[i]?.sourceEventId ?? '<no source_event_id>';\n throw new Error(\n 'cmem-sdk: captureBatch expected a queued outbox row for event ' +\n `${sourceEventId} but received none.`\n );\n }\n return {\n agentEventId: r.event.id,\n generationJobId: r.outbox.id,\n };\n });\n },\n async generate(jobId: string): Promise<CmemGenerateResult> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n if (!provider) {\n throw new Error(\n 'cmem-sdk: no generation provider is configured. ' +\n 'Pass options.provider to createCmemClient (either a constructed ' +\n 'ServerGenerationProvider or { apiKey, model?, provider? }), or set ' +\n 'CLAUDE_MEM_SERVER_PROVIDER + the matching API key environment variable.'\n );\n }\n if (typeof jobId !== 'string' || jobId.trim().length === 0) {\n throw new Error('cmem-sdk: generate requires a non-empty jobId string');\n }\n\n // Step 1: lock the outbox row. queued → processing is the ONLY legal\n // first-step transition (transitionStatus enforces it at the SQL\n // level; generation-jobs.ts:191-194). queued → completed throws.\n // Pre-load via getByIdForScope to give a precise \"not claimable\"\n // error rather than the generic \"transition was not applied\".\n let lockedJob = await repos.observationGenerationJobs.getByIdForScope({\n id: jobId,\n projectId,\n teamId,\n });\n if (!lockedJob) {\n throw new Error(\n `cmem-sdk: generation job ${jobId} not found in scope (projectId=${projectId}, teamId=${teamId})`\n );\n }\n if (lockedJob.status !== 'queued') {\n throw new Error(\n `cmem-sdk: generation job ${jobId} is not claimable (status=\"${lockedJob.status}\"). ` +\n `generate() requires a job in status \"queued\"; the row may have been claimed ` +\n `by another worker, already processed, or terminally failed.`\n );\n }\n\n const transitioned = await repos.observationGenerationJobs.transitionStatus({\n id: jobId,\n projectId,\n teamId,\n status: 'processing',\n lockedBy: 'cmem-sdk',\n });\n if (!transitioned) {\n throw new Error(\n `cmem-sdk: failed to lock generation job ${jobId}; it may have been ` +\n `claimed concurrently by another worker.`\n );\n }\n lockedJob = transitioned;\n\n // Step 2: load events tied to the job. Mirrors\n // ProviderObservationGenerator.loadEvents (lines 483-532) for the\n // 'agent_event' source type — the SDK only ever produces\n // 'agent_event' jobs via capture(), but we still scope the load\n // by tenancy.\n const loadedEvents = [];\n if (lockedJob.agentEventId) {\n const ev = await repos.agentEvents.getByIdForScope({\n id: lockedJob.agentEventId,\n projectId,\n teamId,\n });\n if (ev) loadedEvents.push(ev);\n }\n\n // Step 3: load the project for the prompt's `projectName`.\n const project = await repos.projects.getByIdForTeam(projectId, teamId);\n\n // Steps 4–5: call the provider and persist. Both can throw while the\n // row is in 'processing', and generate()'s queued-only guard can\n // never reclaim a 'processing' row — so any failure here is marked\n // terminally 'failed' (a legal processing→failed transition) before\n // re-throwing. That leaves a diagnosable row with the error recorded\n // in last_error instead of one stuck in 'processing' forever.\n let providerResult: ServerGenerationResult;\n let outcome: Extract<\n Awaited<ReturnType<typeof processGeneratedResponse>>,\n { kind: 'completed' }\n >;\n try {\n // Step 4: call the provider. The lifted core mirrors\n // ProviderObservationGenerator.ts:200-209 — no BullMQ payload, no\n // AbortSignal (consumers control their own timeouts via the\n // provider's fetchImpl), no scope/revocation audit.\n providerResult = await provider.generate({\n job: lockedJob,\n events: loadedEvents,\n project: {\n projectId,\n teamId,\n serverSessionId: lockedJob.serverSessionId,\n projectName: project?.name ?? null,\n },\n });\n\n // Step 5: persist via processGeneratedResponse (the single Postgres\n // transaction that writes observations + observation_sources,\n // then transitions processing → completed). Mirrors\n // ProviderObservationGenerator.ts:211-227 minus the\n // session_summary branch (SDK currently captures only agent_event\n // jobs) and minus the BullMQ identity-context fields.\n const persistInput: Parameters<typeof processGeneratedResponse>[0] = {\n pool,\n job: lockedJob,\n rawText: providerResult.rawText,\n providerLabel: providerResult.providerLabel,\n sourceAdapter: 'sdk',\n };\n if (providerResult.modelId !== undefined) {\n persistInput.modelId = providerResult.modelId;\n }\n const persisted = await processGeneratedResponse(persistInput);\n\n if (persisted.kind === 'parse_error') {\n // The provider returned text we couldn't parse. Surface a clear\n // error; the catch below transitions the row to 'failed'.\n throw new Error(\n `cmem-sdk: generation parse error for job ${persisted.jobId}: ${persisted.reason}`\n );\n }\n outcome = persisted;\n } catch (err) {\n await repos.observationGenerationJobs\n .transitionStatus({\n id: jobId,\n projectId,\n teamId,\n status: 'failed',\n lastError: { message: err instanceof Error ? err.message : String(err) },\n })\n .catch(() => {\n // The row was already moved on (e.g. completed inside step 5's\n // own transaction, or claimed by another worker). Nothing to\n // recover; preserve the original error for the caller.\n });\n throw err;\n }\n\n // Step 6 (Phase 6): index the freshly-persisted observations into\n // Chroma so subsequent client.search() calls can find them. Postgres\n // is already canonical at this point — losing the Chroma index is\n // degraded (next search falls through to FTS), not catastrophic, so\n // indexing failures log + continue instead of throwing. The opposite\n // policy would re-emit an error and roll back the user's already-\n // completed generate(), which is worse than a silent reindex later.\n if (outcome.observations.length > 0) {\n await indexObservationsToChroma(\n chromaSync,\n outcome.observations,\n { projectId, teamId },\n );\n }\n\n const result: CmemGenerateResult = {\n jobId: outcome.jobId,\n observations: outcome.observations,\n providerLabel: providerResult.providerLabel,\n privateContentDetected: outcome.privateContentDetected,\n };\n if (providerResult.modelId !== undefined) {\n result.modelId = providerResult.modelId;\n }\n return result;\n },\n async captureAndGenerate(event: CmemCaptureEvent): Promise<CmemCaptureAndGenerateResult> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n const captured = await client.capture(event);\n const generated = await client.generate(captured.generationJobId);\n return {\n agentEventId: captured.agentEventId,\n generationJobId: captured.generationJobId,\n result: generated,\n };\n },\n async search(input: { query: string; limit?: number }): Promise<CmemSearchResponse> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n const limit = input.limit ?? 10;\n const query = typeof input.query === 'string' ? input.query : '';\n\n // Empty-query path — no semantic intent to express. Mirror the\n // SearchManager filter-only branch (SearchManager.ts:165-176) by\n // returning the most recent observations for this tenant. Chroma is\n // never consulted here, so `chroma: false` (it is NOT degraded — this\n // is the intended filter-only behavior, not a Chroma failure).\n if (query.trim().length === 0) {\n const observations = await repos.observations.listByProject({\n projectId,\n teamId,\n limit,\n });\n return { observations, chroma: false, degraded: false };\n }\n\n // Default path — Chroma semantic. Per plan §6 line 240:\n // queryChromaRaw → UUID doc ids → hydrate via getByIdForScope.\n // We call ChromaMcpManager.callTool directly (not ChromaSync.queryChroma)\n // because the latter routes through deduplicateQueryResults() which\n // is hard-coded to SQLite-shaped `obs_<digits>_<field>` ids and would\n // silently drop our UUID-shaped doc ids. ChromaSync still owns the\n // collection lifecycle (ensureCollectionExists / close); only the\n // query parse differs.\n try {\n await chromaSync.ensureCollectionExists();\n const mgr = ChromaMcpManager.getInstance();\n // Per-tenant `where` filter: doc metadata carries projectId + teamId\n // so a future shared collection can stay safely scoped, even though\n // today's collection name is already `cm__<projectId>`.\n const whereFilter = {\n $and: [\n { projectId },\n { teamId },\n ],\n };\n const raw = (await mgr.callTool('chroma_query_documents', {\n collection_name: chromaSync.getCollectionName(),\n query_texts: [query],\n n_results: limit,\n where: whereFilter,\n include: ['documents', 'metadatas', 'distances'],\n })) as {\n ids?: string[][];\n documents?: string[][];\n metadatas?: Array<Array<Record<string, unknown> | null>>;\n distances?: number[][];\n };\n\n const docIds = raw?.ids?.[0] ?? [];\n if (docIds.length === 0) {\n return { observations: [], chroma: true, degraded: false };\n }\n\n // Hydrate via getByIdForScope. We preserve Chroma's rank order\n // because semantic distance is the whole reason we called Chroma.\n // listByProject would reorder by created_at and undo that.\n const hydrated: PostgresObservation[] = [];\n for (const docId of docIds) {\n if (typeof docId !== 'string') continue;\n const obs = await repos.observations.getByIdForScope({\n id: docId,\n projectId,\n teamId,\n });\n if (obs) hydrated.push(obs);\n }\n return { observations: hydrated, chroma: true, degraded: false };\n } catch (err) {\n // Runtime safety net — Chroma transiently died (subprocess exit,\n // ECONNREFUSED, etc.). Mirrors SearchManager.ts:255's catch-and-\n // degrade-once pattern. This is NOT a feature toggle: a successful\n // run never enters this branch. We log loudly so an operator can\n // investigate the uvx chroma-mcp subprocess. See plan §6 line 242\n // and the 2026-05-29 correction log.\n logger.error(\n 'CHROMA',\n 'semantic search failed; returning degraded FTS results — investigate uvx chroma-mcp',\n { projectId, teamId, query },\n err as Error,\n );\n const observations = await repos.observations.search({\n projectId,\n teamId,\n query,\n limit,\n });\n return {\n observations,\n chroma: false,\n degraded: true,\n error: { message: 'chroma-mcp transient failure' },\n };\n }\n },\n async context(input: { query: string; limit?: number }): Promise<CmemContextResponse> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n // Reuse search so the Chroma / degraded-FTS branching lives in exactly\n // one place. The context-pack format below copies the\n // ServerV1PostgresRoutes /v1/context handler verbatim\n // (ServerV1PostgresRoutes.ts:892-895): map → filter non-empty → join.\n const result = await client.search(input);\n const context = result.observations\n .map(observation => observation.content)\n .filter(text => typeof text === 'string' && text.length > 0)\n .join('\\n\\n');\n return {\n observations: result.observations,\n context,\n degraded: result.degraded,\n };\n },\n async startSession(input: CmemStartSessionInput = {}): Promise<CmemSessionInfo> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n const createInput: Parameters<typeof repos.sessions.create>[0] = {\n projectId,\n teamId,\n };\n assignDefined(createInput as unknown as Record<string, unknown>, input as unknown as Record<string, unknown>, [\n 'externalSessionId',\n 'contentSessionId',\n 'agentId',\n 'agentType',\n 'platformSource',\n 'metadata',\n ]);\n const session = await repos.sessions.create(createInput);\n return { serverSessionId: session.id };\n },\n async endSession(serverSessionId: string): Promise<void> {\n if (closed) {\n throw new Error('cmem-sdk: client is closed');\n }\n // endSession is idempotent (server-sessions.ts:145-162). It returns\n // null only if the (id, projectId, teamId) tuple does not exist;\n // surface that as a clear error so callers don't silently no-op.\n const updated = await repos.sessions.endSession({\n id: serverSessionId,\n projectId,\n teamId,\n });\n if (!updated) {\n throw new Error(\n `cmem-sdk: endSession could not find server_session ${serverSessionId} ` +\n `for tenant (projectId=${projectId}, teamId=${teamId})`\n );\n }\n },\n async close() {\n if (closed) {\n return;\n }\n closed = true;\n await chromaSync.close().catch(() => {});\n if (ownsPool) {\n await closePostgresPool(pool).catch(() => {});\n }\n },\n };\n\n return client;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport pg, { type Pool as PgPool, type PoolClient as PgPoolClient } from 'pg';\nimport { parsePostgresConfig, type PostgresConfig } from './config.js';\n\nconst { Pool } = pg;\n\nexport type PostgresPool = PgPool;\nexport type PostgresPoolClient = PgPoolClient;\n\nlet sharedPool: PostgresPool | null = null;\n\nexport function createPostgresPool(config: PostgresConfig): PostgresPool {\n return new Pool({\n connectionString: config.connectionString,\n max: config.max,\n idleTimeoutMillis: config.idleTimeoutMillis,\n connectionTimeoutMillis: config.connectionTimeoutMillis,\n statement_timeout: config.statementTimeoutMillis,\n ssl: config.ssl\n });\n}\n\nexport function getSharedPostgresPool(options: { requireDatabaseUrl?: boolean } = {}): PostgresPool {\n if (sharedPool) {\n return sharedPool;\n }\n const config = parsePostgresConfig({ requireDatabaseUrl: options.requireDatabaseUrl ?? true });\n if (!config) {\n throw new Error('Postgres requires CLAUDE_MEM_SERVER_DATABASE_URL');\n }\n sharedPool = createPostgresPool(config);\n return sharedPool;\n}\n\nexport async function checkPostgresHealth(pool: PostgresPool): Promise<boolean> {\n try {\n await pool.query('SELECT 1');\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function withPostgresTransaction<T>(\n pool: PostgresPool,\n fn: (client: PostgresPoolClient) => Promise<T>\n): Promise<T> {\n const client = await pool.connect();\n try {\n await client.query('BEGIN');\n const result = await fn(client);\n await client.query('COMMIT');\n return result;\n } catch (error) {\n await client.query('ROLLBACK');\n throw error;\n } finally {\n client.release();\n }\n}\n\nexport async function closePostgresPool(pool: PostgresPool): Promise<void> {\n if (pool === sharedPool) {\n sharedPool = null;\n }\n await pool.end();\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nexport interface PostgresConfig {\n connectionString: string;\n max: number;\n idleTimeoutMillis: number;\n connectionTimeoutMillis: number;\n statementTimeoutMillis: number;\n ssl: boolean | { rejectUnauthorized: boolean };\n}\n\nexport interface ParsePostgresConfigOptions {\n env?: NodeJS.ProcessEnv;\n requireDatabaseUrl?: boolean;\n}\n\nconst DEFAULT_POOL_MAX = 10;\nconst DEFAULT_IDLE_TIMEOUT_MS = 30_000;\nconst DEFAULT_CONNECTION_TIMEOUT_MS = 5_000;\nconst DEFAULT_STATEMENT_TIMEOUT_MS = 30_000;\n\nexport function getPostgresDatabaseUrl(env: NodeJS.ProcessEnv = process.env): string | null {\n return env.CLAUDE_MEM_SERVER_DATABASE_URL || null;\n}\n\nexport function parsePostgresConfig(options: ParsePostgresConfigOptions = {}): PostgresConfig | null {\n const env = options.env ?? process.env;\n const connectionString = getPostgresDatabaseUrl(env);\n if (!connectionString) {\n if (options.requireDatabaseUrl) {\n throw new Error('Postgres requires CLAUDE_MEM_SERVER_DATABASE_URL');\n }\n return null;\n }\n\n return {\n connectionString,\n max: parsePositiveInt(env.CLAUDE_MEM_POSTGRES_POOL_MAX, DEFAULT_POOL_MAX),\n idleTimeoutMillis: parsePositiveInt(env.CLAUDE_MEM_POSTGRES_IDLE_TIMEOUT_MS, DEFAULT_IDLE_TIMEOUT_MS),\n connectionTimeoutMillis: parsePositiveInt(env.CLAUDE_MEM_POSTGRES_CONNECTION_TIMEOUT_MS, DEFAULT_CONNECTION_TIMEOUT_MS),\n statementTimeoutMillis: parsePositiveInt(env.CLAUDE_MEM_POSTGRES_STATEMENT_TIMEOUT_MS, DEFAULT_STATEMENT_TIMEOUT_MS),\n ssl: parseSsl(connectionString, env)\n };\n}\n\nfunction parsePositiveInt(value: string | undefined, fallback: number): number {\n if (!value) {\n return fallback;\n }\n const parsed = Number.parseInt(value, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\nfunction parseSsl(connectionString: string, env: NodeJS.ProcessEnv): boolean | { rejectUnauthorized: boolean } {\n if (env.CLAUDE_MEM_POSTGRES_SSL === 'disable' || env.PGSSLMODE === 'disable') {\n return false;\n }\n if (env.CLAUDE_MEM_POSTGRES_SSL === 'require' || env.PGSSLMODE === 'require') {\n return { rejectUnauthorized: false };\n }\n\n try {\n const url = new URL(connectionString);\n if (url.searchParams.get('sslmode') === 'require') {\n return { rejectUnauthorized: false };\n }\n } catch {\n return false;\n }\n\n return false;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { PostgresQueryable } from './utils.js';\n\nexport const SERVER_POSTGRES_SCHEMA_VERSION = 1;\n\n// Phase 1b (cmem-sdk rename): the TS constant is renamed but the table-name\n// strings remain on `server_beta_*` since they are persisted DDL identifiers.\n// Plan §1d will migrate the table names in a coordinated DDL change.\nexport const SERVER_POSTGRES_TABLES = [\n 'server_beta_schema_migrations',\n 'teams',\n 'projects',\n 'team_members',\n 'api_keys',\n 'audit_log',\n 'server_sessions',\n 'agent_events',\n 'observation_generation_jobs',\n 'observations',\n 'observation_sources',\n 'observation_generation_job_events',\n 'usage_events',\n 'rate_limit_counters'\n] as const;\n\nexport async function bootstrapServerPostgresSchema(client: PostgresQueryable): Promise<void> {\n if (isPostgresPool(client)) {\n const poolClient = await client.connect();\n try {\n await bootstrapServerPostgresSchema(poolClient);\n } finally {\n poolClient.release();\n }\n return;\n }\n\n await client.query('BEGIN');\n try {\n await client.query(PHASE_1_SCHEMA_SQL);\n await client.query(\n `\n INSERT INTO server_beta_schema_migrations (version, description)\n VALUES ($1, $2)\n ON CONFLICT (version) DO NOTHING\n `,\n [SERVER_POSTGRES_SCHEMA_VERSION, 'phase 1 postgres observation storage foundation']\n );\n await client.query('COMMIT');\n } catch (error) {\n await client.query('ROLLBACK');\n throw error;\n }\n}\n\ninterface PostgresPoolLike extends PostgresQueryable {\n connect(): Promise<PostgresQueryable & { release(): void }>;\n}\n\nfunction isPostgresPool(client: PostgresQueryable): client is PostgresPoolLike {\n const candidate = client as {\n connect?: unknown;\n release?: unknown;\n totalCount?: unknown;\n idleCount?: unknown;\n waitingCount?: unknown;\n };\n return (\n typeof candidate.connect === 'function'\n && typeof candidate.release !== 'function'\n && typeof candidate.totalCount === 'number'\n && typeof candidate.idleCount === 'number'\n && typeof candidate.waitingCount === 'number'\n );\n}\n\nconst PHASE_1_SCHEMA_SQL = `\nCREATE TABLE IF NOT EXISTS server_beta_schema_migrations (\n version INTEGER PRIMARY KEY,\n description TEXT NOT NULL,\n applied_at TIMESTAMPTZ NOT NULL DEFAULT now()\n);\n\nCREATE TABLE IF NOT EXISTS teams (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now()\n);\n\nCREATE TABLE IF NOT EXISTS projects (\n id TEXT PRIMARY KEY,\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n name TEXT NOT NULL,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n UNIQUE (id, team_id)\n);\n\nCREATE TABLE IF NOT EXISTS team_members (\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n user_id TEXT NOT NULL,\n role TEXT NOT NULL,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n PRIMARY KEY (team_id, user_id)\n);\n\nCREATE TABLE IF NOT EXISTS api_keys (\n id TEXT PRIMARY KEY,\n key_hash TEXT NOT NULL UNIQUE,\n team_id TEXT REFERENCES teams(id) ON DELETE CASCADE,\n project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,\n actor_id TEXT NOT NULL,\n scopes JSONB NOT NULL DEFAULT '[]'::jsonb,\n revoked_at TIMESTAMPTZ,\n expires_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n CHECK (project_id IS NULL OR team_id IS NOT NULL),\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS audit_log (\n id TEXT PRIMARY KEY,\n team_id TEXT REFERENCES teams(id) ON DELETE SET NULL,\n project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,\n actor_id TEXT,\n api_key_id TEXT REFERENCES api_keys(id) ON DELETE SET NULL,\n action TEXT NOT NULL,\n resource_type TEXT NOT NULL,\n resource_id TEXT,\n details JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n CHECK (project_id IS NULL OR team_id IS NOT NULL),\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE SET NULL\n);\n\nCREATE TABLE IF NOT EXISTS server_sessions (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n external_session_id TEXT,\n idempotency_key TEXT,\n content_session_id TEXT,\n agent_id TEXT,\n agent_type TEXT,\n platform_source TEXT,\n generation_status TEXT NOT NULL DEFAULT 'idle',\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n started_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n ended_at TIMESTAMPTZ,\n last_generated_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS agent_events (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n server_session_id TEXT REFERENCES server_sessions(id) ON DELETE SET NULL,\n source_adapter TEXT NOT NULL,\n source_event_id TEXT,\n idempotency_key TEXT NOT NULL,\n event_type TEXT NOT NULL,\n payload JSONB NOT NULL,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n occurred_at TIMESTAMPTZ NOT NULL,\n received_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n UNIQUE (idempotency_key),\n UNIQUE (id, project_id, team_id),\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS observation_generation_jobs (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n agent_event_id TEXT REFERENCES agent_events(id) ON DELETE CASCADE,\n source_type TEXT NOT NULL CHECK (source_type IN ('agent_event', 'session_summary', 'observation_reindex')),\n source_id TEXT NOT NULL,\n server_session_id TEXT REFERENCES server_sessions(id) ON DELETE SET NULL,\n job_type TEXT NOT NULL,\n status TEXT NOT NULL CHECK (status IN ('queued', 'processing', 'completed', 'failed', 'cancelled')),\n idempotency_key TEXT NOT NULL UNIQUE,\n bullmq_job_id TEXT UNIQUE,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n next_attempt_at TIMESTAMPTZ,\n locked_at TIMESTAMPTZ,\n locked_by TEXT,\n completed_at TIMESTAMPTZ,\n failed_at TIMESTAMPTZ,\n cancelled_at TIMESTAMPTZ,\n last_error JSONB,\n payload JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n CHECK (\n (source_type = 'agent_event' AND agent_event_id IS NOT NULL AND source_id = agent_event_id)\n OR\n (source_type = 'session_summary' AND agent_event_id IS NULL AND server_session_id IS NOT NULL AND source_id = server_session_id)\n OR\n (source_type = 'observation_reindex' AND agent_event_id IS NULL)\n ),\n FOREIGN KEY (agent_event_id, project_id, team_id) REFERENCES agent_events(id, project_id, team_id) ON DELETE CASCADE,\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS observations (\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n server_session_id TEXT REFERENCES server_sessions(id) ON DELETE SET NULL,\n kind TEXT NOT NULL DEFAULT 'observation',\n content TEXT NOT NULL,\n content_search TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', content)) STORED,\n generation_key TEXT,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n embedding JSONB,\n created_by_job_id TEXT REFERENCES observation_generation_jobs(id) ON DELETE SET NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n FOREIGN KEY (project_id, team_id) REFERENCES projects(id, team_id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS observation_sources (\n id TEXT PRIMARY KEY,\n observation_id TEXT NOT NULL REFERENCES observations(id) ON DELETE CASCADE,\n agent_event_id TEXT REFERENCES agent_events(id) ON DELETE CASCADE,\n generation_job_id TEXT REFERENCES observation_generation_jobs(id) ON DELETE SET NULL,\n source_type TEXT NOT NULL CHECK (source_type IN ('agent_event', 'session_summary', 'observation_reindex', 'manual')),\n source_id TEXT NOT NULL,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n UNIQUE (observation_id, source_type, source_id),\n UNIQUE (source_type, source_id, generation_job_id, observation_id),\n CHECK (\n (source_type = 'agent_event' AND agent_event_id IS NOT NULL AND source_id = agent_event_id)\n OR\n (source_type <> 'agent_event' AND agent_event_id IS NULL)\n )\n);\n\nCREATE TABLE IF NOT EXISTS observation_generation_job_events (\n id TEXT PRIMARY KEY,\n generation_job_id TEXT NOT NULL REFERENCES observation_generation_jobs(id) ON DELETE CASCADE,\n event_type TEXT NOT NULL CHECK (event_type IN ('queued', 'enqueued', 'processing', 'retry_scheduled', 'completed', 'failed', 'cancelled')),\n status_after TEXT NOT NULL CHECK (status_after IN ('queued', 'processing', 'completed', 'failed', 'cancelled')),\n attempt INTEGER NOT NULL DEFAULT 0,\n details JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now()\n);\n\nCREATE INDEX IF NOT EXISTS idx_agent_events_project_session ON agent_events(project_id, server_session_id, occurred_at);\nALTER TABLE server_sessions ADD COLUMN IF NOT EXISTS idempotency_key TEXT;\nALTER TABLE server_sessions DROP CONSTRAINT IF EXISTS server_sessions_project_id_external_session_id_key;\n-- #2560 — platform_source on agent_events (consistent with server_sessions and\n-- the plan-09 scoping): which platform produced the event (claude-code,\n-- opencode, cursor, ...). Idempotent so an existing DB upgrades in place.\nALTER TABLE agent_events ADD COLUMN IF NOT EXISTS platform_source TEXT;\nCREATE INDEX IF NOT EXISTS idx_agent_events_platform_source\n ON agent_events(team_id, project_id, platform_source, occurred_at)\n WHERE platform_source IS NOT NULL;\nCREATE INDEX IF NOT EXISTS idx_server_sessions_platform_source\n ON server_sessions(team_id, project_id, platform_source, started_at)\n WHERE platform_source IS NOT NULL;\nALTER TABLE observations ADD COLUMN IF NOT EXISTS content_search TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', content)) STORED;\nALTER TABLE observations DROP CONSTRAINT IF EXISTS observations_generation_key_key;\nALTER TABLE observation_generation_jobs DROP CONSTRAINT IF EXISTS observation_generation_jobs_source_type_source_id_job_type_key;\nCREATE UNIQUE INDEX IF NOT EXISTS idx_server_sessions_project_idempotency\n ON server_sessions(project_id, idempotency_key)\n WHERE idempotency_key IS NOT NULL;\nCREATE UNIQUE INDEX IF NOT EXISTS idx_server_sessions_external_session_legacy\n ON server_sessions(project_id, external_session_id)\n WHERE external_session_id IS NOT NULL AND platform_source IS NULL;\nCREATE UNIQUE INDEX IF NOT EXISTS idx_server_sessions_external_session_platform\n ON server_sessions(project_id, platform_source, external_session_id)\n WHERE external_session_id IS NOT NULL AND platform_source IS NOT NULL;\nDROP INDEX IF EXISTS idx_server_sessions_content_session;\n-- Supports platform-aware session linkage lookup on the /v1/events ingest path.\nCREATE INDEX IF NOT EXISTS idx_server_sessions_content_session_platform\n ON server_sessions(team_id, project_id, platform_source, content_session_id, started_at DESC)\n WHERE content_session_id IS NOT NULL;\nCREATE UNIQUE INDEX IF NOT EXISTS idx_observations_generation_key_scope\n ON observations(team_id, project_id, generation_key)\n WHERE generation_key IS NOT NULL;\nCREATE UNIQUE INDEX IF NOT EXISTS idx_observation_jobs_source_scope\n ON observation_generation_jobs(team_id, project_id, source_type, source_id, job_type);\nCREATE INDEX IF NOT EXISTS idx_projects_team ON projects(team_id, id);\nCREATE INDEX IF NOT EXISTS idx_agent_events_team_project ON agent_events(team_id, project_id, occurred_at);\nCREATE INDEX IF NOT EXISTS idx_observations_project_session ON observations(project_id, server_session_id, created_at);\nCREATE INDEX IF NOT EXISTS idx_observations_team_project ON observations(team_id, project_id, created_at);\nCREATE INDEX IF NOT EXISTS idx_observations_content_search ON observations USING GIN (content_search);\nCREATE INDEX IF NOT EXISTS idx_observation_sources_event ON observation_sources(agent_event_id);\nCREATE INDEX IF NOT EXISTS idx_observation_sources_source ON observation_sources(source_type, source_id);\nCREATE INDEX IF NOT EXISTS idx_observation_jobs_status_next_attempt ON observation_generation_jobs(status, next_attempt_at, created_at);\nCREATE INDEX IF NOT EXISTS idx_observation_jobs_team_project ON observation_generation_jobs(team_id, project_id, status, created_at);\nCREATE INDEX IF NOT EXISTS idx_observation_jobs_event ON observation_generation_jobs(agent_event_id);\nCREATE INDEX IF NOT EXISTS idx_observation_jobs_source ON observation_generation_jobs(source_type, source_id);\nCREATE INDEX IF NOT EXISTS idx_observation_job_events_job_created ON observation_generation_job_events(generation_job_id, created_at);\nCREATE INDEX IF NOT EXISTS idx_audit_log_scope_created ON audit_log(project_id, team_id, created_at);\n\n-- Usage metering: append-only per-team usage, aggregated for quotas + billing.\n-- kind is open-ended ('request', 'tokens_in', 'tokens_out', 'observation', ...).\nCREATE TABLE IF NOT EXISTS usage_events (\n id TEXT PRIMARY KEY,\n team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,\n project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,\n kind TEXT NOT NULL,\n quantity BIGINT NOT NULL DEFAULT 1,\n metadata JSONB NOT NULL DEFAULT '{}'::jsonb,\n created_at TIMESTAMPTZ NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS idx_usage_events_team_created ON usage_events(team_id, created_at);\nCREATE INDEX IF NOT EXISTS idx_usage_events_team_kind_created ON usage_events(team_id, kind, created_at);\n\n-- Fixed-window rate-limit counters. subject_id is the api key id (per-key limit).\nCREATE TABLE IF NOT EXISTS rate_limit_counters (\n subject_id TEXT NOT NULL,\n window_start TIMESTAMPTZ NOT NULL,\n count BIGINT NOT NULL DEFAULT 0,\n PRIMARY KEY (subject_id, window_start)\n);\nCREATE INDEX IF NOT EXISTS idx_rate_limit_counters_window ON rate_limit_counters(window_start);\n`;\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { createHash, randomUUID } from 'crypto';\nimport type { QueryResult, QueryResultRow } from 'pg';\n\nexport type JsonObject = Record<string, unknown>;\nexport type JsonValue = unknown;\n\nexport interface PostgresQueryable {\n query<T extends QueryResultRow = QueryResultRow>(text: string, values?: unknown[]): Promise<QueryResult<T>>;\n}\n\nexport function newId(): string {\n return randomUUID();\n}\n\nexport function toJsonObject(value: unknown): JsonObject {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return value as JsonObject;\n }\n return {};\n}\n\nexport function toJsonArray(value: unknown): unknown[] {\n return Array.isArray(value) ? value : [];\n}\n\nexport function toEpoch(value: Date | string | number): number {\n if (typeof value === 'number') {\n return value;\n }\n return new Date(value).getTime();\n}\n\nexport function toDate(value: Date | string | number | null | undefined): Date | null {\n if (value == null) {\n return null;\n }\n return value instanceof Date ? value : new Date(value);\n}\n\nexport async function queryOne<T extends QueryResultRow>(\n client: PostgresQueryable,\n text: string,\n values: unknown[] = []\n): Promise<T | null> {\n const result = await client.query<T>(text, values);\n return result.rows[0] ?? null;\n}\n\nexport async function assertProjectOwnership(\n client: PostgresQueryable,\n projectId: string,\n teamId: string\n): Promise<void> {\n const row = await queryOne<{ id: string }>(\n client,\n 'SELECT id FROM projects WHERE id = $1 AND team_id = $2',\n [projectId, teamId]\n );\n if (!row) {\n throw new Error('project_id must belong to team_id');\n }\n}\n\nexport async function assertSessionOwnership(\n client: PostgresQueryable,\n serverSessionId: string,\n projectId: string,\n teamId: string\n): Promise<void> {\n const row = await queryOne<{ id: string }>(\n client,\n 'SELECT id FROM server_sessions WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [serverSessionId, projectId, teamId]\n );\n if (!row) {\n throw new Error('server_session_id must belong to project_id and team_id');\n }\n}\n\nexport function canonicalJson(value: unknown): string {\n return JSON.stringify(sortJson(value));\n}\n\nexport function deterministicKey(parts: readonly unknown[]): string {\n const fingerprint = createHash('sha256')\n .update(canonicalJson(parts))\n .digest('hex');\n return fingerprint;\n}\n\nfunction sortJson(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortJson);\n }\n if (value && typeof value === 'object') {\n const record = value as Record<string, unknown>;\n return Object.keys(record)\n .sort()\n .reduce<Record<string, unknown>>((acc, key) => {\n acc[key] = sortJson(record[key]);\n return acc;\n }, {});\n }\n return value;\n}\n","export const DEFAULT_PLATFORM_SOURCE = 'claude';\n\nfunction sanitizeRawSource(value: string): string {\n return value.trim().toLowerCase().replace(/\\s+/g, '-');\n}\n\nexport function normalizePlatformSource(value?: string | null): string {\n if (!value) return DEFAULT_PLATFORM_SOURCE;\n\n const source = sanitizeRawSource(value);\n if (!source) return DEFAULT_PLATFORM_SOURCE;\n\n if (source === 'transcript') return 'codex';\n if (source.includes('codex')) return 'codex';\n if (source.includes('cursor')) return 'cursor';\n if (source.includes('claude')) return 'claude';\n\n return source;\n}\n\nexport function normalizePlatformSourceOrNull(value?: string | null): string | null {\n if (typeof value !== 'string') return null;\n return normalizePlatformSource(value);\n}\n\nexport function sortPlatformSources(sources: string[]): string[] {\n const priority = ['claude', 'codex', 'cursor'];\n\n return [...sources].sort((a, b) => {\n const aPriority = priority.indexOf(a);\n const bPriority = priority.indexOf(b);\n\n if (aPriority !== -1 || bPriority !== -1) {\n if (aPriority === -1) return 1;\n if (bPriority === -1) return -1;\n return aPriority - bPriority;\n }\n\n return a.localeCompare(b);\n });\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, JsonValue, PostgresQueryable } from './utils.js';\nimport {\n assertProjectOwnership,\n assertSessionOwnership,\n canonicalJson,\n deterministicKey,\n newId,\n queryOne,\n toEpoch,\n toJsonObject\n} from './utils.js';\nimport { normalizePlatformSourceOrNull } from '../../shared/platform-source.js';\n\nexport interface PostgresAgentEvent {\n id: string;\n projectId: string;\n teamId: string;\n serverSessionId: string | null;\n sourceAdapter: string;\n sourceEventId: string | null;\n idempotencyKey: string;\n eventType: string;\n platformSource: string | null;\n payload: JsonValue;\n metadata: JsonObject;\n occurredAtEpoch: number;\n receivedAtEpoch: number;\n createdAtEpoch: number;\n}\n\nexport interface CreatePostgresAgentEventInput {\n id?: string;\n projectId: string;\n teamId: string;\n serverSessionId?: string | null;\n contentSessionId?: string | null;\n sourceAdapter: string;\n sourceEventId?: string | null;\n eventType: string;\n // #2560 — which platform produced the event (claude-code, opencode, ...).\n // Persisted on agent_events for plan-09 scoping. Optional; null when unknown.\n platformSource?: string | null;\n payload?: JsonValue;\n metadata?: JsonObject;\n occurredAt: Date | string | number;\n}\n\ninterface AgentEventRow {\n id: string;\n project_id: string;\n team_id: string;\n server_session_id: string | null;\n source_adapter: string;\n source_event_id: string | null;\n idempotency_key: string;\n event_type: string;\n platform_source: string | null;\n payload: unknown;\n metadata: unknown;\n occurred_at: Date;\n received_at: Date;\n created_at: Date;\n}\n\nexport class PostgresAgentEventsRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: CreatePostgresAgentEventInput): Promise<PostgresAgentEvent> {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n if (input.serverSessionId) {\n await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);\n }\n const idempotencyKey = buildAgentEventIdempotencyKey(input);\n const platformSource = normalizePlatformSourceOrNull(input.platformSource);\n const row = await queryOne<AgentEventRow>(\n this.client,\n `\n INSERT INTO agent_events (\n id, project_id, team_id, server_session_id, source_adapter,\n source_event_id, idempotency_key, event_type, platform_source, payload, metadata, occurred_at\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb, $11::jsonb, $12)\n ON CONFLICT (idempotency_key) DO UPDATE SET\n metadata = agent_events.metadata || excluded.metadata,\n platform_source = COALESCE(excluded.platform_source, agent_events.platform_source)\n RETURNING *\n `,\n [\n input.id ?? newId(),\n input.projectId,\n input.teamId,\n input.serverSessionId ?? null,\n input.sourceAdapter,\n input.sourceEventId ?? null,\n idempotencyKey,\n input.eventType,\n platformSource,\n JSON.stringify(input.payload ?? {}),\n JSON.stringify(input.metadata ?? {}),\n new Date(input.occurredAt)\n ]\n );\n return mapAgentEventRow(row!);\n }\n\n async getByIdForScope(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresAgentEvent | null> {\n const row = await queryOne<AgentEventRow>(\n this.client,\n 'SELECT * FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.id, input.projectId, input.teamId]\n );\n return row ? mapAgentEventRow(row) : null;\n }\n\n async listByProject(input: {\n projectId: string;\n teamId: string;\n serverSessionId?: string | null;\n limit?: number;\n }): Promise<PostgresAgentEvent[]> {\n const result = await this.client.query<AgentEventRow>(\n `\n SELECT * FROM agent_events\n WHERE project_id = $1\n AND team_id = $2\n AND ($3::text IS NULL OR server_session_id = $3)\n ORDER BY occurred_at DESC\n LIMIT $4\n `,\n [input.projectId, input.teamId, input.serverSessionId ?? null, input.limit ?? 100]\n );\n return result.rows.map(mapAgentEventRow);\n }\n}\n\nexport function buildAgentEventIdempotencyKey(input: {\n teamId: string;\n projectId: string;\n sourceAdapter: string;\n sourceEventId?: string | null;\n serverSessionId?: string | null;\n contentSessionId?: string | null;\n eventType: string;\n platformSource?: string | null;\n occurredAt: Date | string | number;\n payload?: JsonValue;\n}): string {\n const platformSource = normalizePlatformSourceOrNull(input.platformSource);\n const platformScope = platformSource ? [platformSource] : [];\n\n if (input.sourceEventId) {\n return `agent_event:v1:${deterministicKey([\n input.teamId,\n input.projectId,\n input.sourceAdapter,\n ...platformScope,\n input.sourceEventId\n ])}`;\n }\n\n // Use contentSessionId (stable, client-provided) over serverSessionId, which is\n // resolved lazily at ingest and can be NULL on a first delivery but non-NULL on a\n // retry — that would drift the key and create duplicate events. Falls back to\n // serverSessionId for events that don't carry a contentSessionId.\n return `agent_event:v1:${deterministicKey([\n input.teamId,\n input.projectId,\n input.sourceAdapter,\n ...platformScope,\n input.contentSessionId ?? input.serverSessionId ?? null,\n input.eventType,\n new Date(input.occurredAt).toISOString(),\n canonicalJson(input.payload ?? {})\n ])}`;\n}\n\nfunction mapAgentEventRow(row: AgentEventRow): PostgresAgentEvent {\n return {\n id: row.id,\n projectId: row.project_id,\n teamId: row.team_id,\n serverSessionId: row.server_session_id,\n sourceAdapter: row.source_adapter,\n sourceEventId: row.source_event_id,\n idempotencyKey: row.idempotency_key,\n eventType: row.event_type,\n platformSource: row.platform_source,\n payload: row.payload,\n metadata: toJsonObject(row.metadata),\n occurredAtEpoch: toEpoch(row.occurred_at),\n receivedAtEpoch: toEpoch(row.received_at),\n createdAtEpoch: toEpoch(row.created_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, PostgresQueryable } from './utils.js';\nimport { assertProjectOwnership, newId, queryOne, toDate, toEpoch, toJsonArray, toJsonObject } from './utils.js';\n\nexport interface PostgresApiKey {\n id: string;\n keyHash: string;\n teamId: string | null;\n projectId: string | null;\n actorId: string;\n scopes: unknown[];\n revokedAtEpoch: number | null;\n expiresAtEpoch: number | null;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\nexport interface PostgresAuditLog {\n id: string;\n teamId: string | null;\n projectId: string | null;\n actorId: string | null;\n apiKeyId: string | null;\n action: string;\n resourceType: string;\n resourceId: string | null;\n details: JsonObject;\n createdAtEpoch: number;\n}\n\ninterface ApiKeyRow {\n id: string;\n key_hash: string;\n team_id: string | null;\n project_id: string | null;\n actor_id: string;\n scopes: unknown;\n revoked_at: Date | null;\n expires_at: Date | null;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface AuditLogRow {\n id: string;\n team_id: string | null;\n project_id: string | null;\n actor_id: string | null;\n api_key_id: string | null;\n action: string;\n resource_type: string;\n resource_id: string | null;\n details: unknown;\n created_at: Date;\n}\n\nexport class PostgresAuthRepository {\n constructor(private client: PostgresQueryable) {}\n\n async createApiKey(input: {\n id?: string;\n keyHash: string;\n teamId?: string | null;\n projectId?: string | null;\n actorId: string;\n scopes?: unknown[];\n expiresAt?: Date | null;\n }): Promise<PostgresApiKey> {\n if (input.projectId && input.teamId) {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n }\n const id = input.id ?? newId();\n const row = await queryOne<ApiKeyRow>(\n this.client,\n `\n INSERT INTO api_keys (id, key_hash, team_id, project_id, actor_id, scopes, expires_at)\n VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7)\n RETURNING *\n `,\n [\n id,\n input.keyHash,\n input.teamId ?? null,\n input.projectId ?? null,\n input.actorId,\n JSON.stringify(input.scopes ?? []),\n input.expiresAt ?? null\n ]\n );\n return mapApiKeyRow(row!);\n }\n\n async createAuditLog(input: {\n id?: string;\n teamId?: string | null;\n projectId?: string | null;\n actorId?: string | null;\n apiKeyId?: string | null;\n action: string;\n resourceType: string;\n resourceId?: string | null;\n details?: JsonObject;\n }): Promise<PostgresAuditLog> {\n if (input.projectId && input.teamId) {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n }\n const id = input.id ?? newId();\n const row = await queryOne<AuditLogRow>(\n this.client,\n `\n INSERT INTO audit_log (\n id, team_id, project_id, actor_id, api_key_id, action,\n resource_type, resource_id, details\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb)\n RETURNING *\n `,\n [\n id,\n input.teamId ?? null,\n input.projectId ?? null,\n input.actorId ?? null,\n input.apiKeyId ?? null,\n input.action,\n input.resourceType,\n input.resourceId ?? null,\n JSON.stringify(input.details ?? {})\n ]\n );\n return mapAuditLogRow(row!);\n }\n\n async getApiKeyByHash(keyHash: string): Promise<PostgresApiKey | null> {\n const row = await queryOne<ApiKeyRow>(this.client, 'SELECT * FROM api_keys WHERE key_hash = $1', [keyHash]);\n return row ? mapApiKeyRow(row) : null;\n }\n}\n\nfunction mapApiKeyRow(row: ApiKeyRow): PostgresApiKey {\n return {\n id: row.id,\n keyHash: row.key_hash,\n teamId: row.team_id,\n projectId: row.project_id,\n actorId: row.actor_id,\n scopes: toJsonArray(row.scopes),\n revokedAtEpoch: toDate(row.revoked_at)?.getTime() ?? null,\n expiresAtEpoch: toDate(row.expires_at)?.getTime() ?? null,\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n\nfunction mapAuditLogRow(row: AuditLogRow): PostgresAuditLog {\n return {\n id: row.id,\n teamId: row.team_id,\n projectId: row.project_id,\n actorId: row.actor_id,\n apiKeyId: row.api_key_id,\n action: row.action,\n resourceType: row.resource_type,\n resourceId: row.resource_id,\n details: toJsonObject(row.details),\n createdAtEpoch: toEpoch(row.created_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, PostgresQueryable } from './utils.js';\nimport {\n assertProjectOwnership,\n assertSessionOwnership,\n deterministicKey,\n newId,\n queryOne,\n toDate,\n toEpoch,\n toJsonObject\n} from './utils.js';\n\nexport type ObservationGenerationJobSourceType = 'agent_event' | 'session_summary' | 'observation_reindex';\nexport type ObservationGenerationJobStatus = 'queued' | 'processing' | 'completed' | 'failed' | 'cancelled';\nexport type ObservationGenerationJobEventType =\n | 'queued'\n | 'enqueued'\n | 'processing'\n | 'retry_scheduled'\n | 'completed'\n | 'failed'\n | 'cancelled';\n\nexport interface PostgresObservationGenerationJob {\n id: string;\n projectId: string;\n teamId: string;\n agentEventId: string | null;\n sourceType: ObservationGenerationJobSourceType;\n sourceId: string;\n serverSessionId: string | null;\n jobType: string;\n status: ObservationGenerationJobStatus;\n idempotencyKey: string;\n bullmqJobId: string | null;\n attempts: number;\n maxAttempts: number;\n nextAttemptAtEpoch: number | null;\n lockedAtEpoch: number | null;\n lockedBy: string | null;\n completedAtEpoch: number | null;\n failedAtEpoch: number | null;\n cancelledAtEpoch: number | null;\n lastError: JsonObject | null;\n payload: JsonObject;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\nexport interface PostgresObservationGenerationJobEvent {\n id: string;\n generationJobId: string;\n eventType: ObservationGenerationJobEventType;\n statusAfter: ObservationGenerationJobStatus;\n attempt: number;\n details: JsonObject;\n createdAtEpoch: number;\n}\n\ninterface JobRow {\n id: string;\n project_id: string;\n team_id: string;\n agent_event_id: string | null;\n source_type: ObservationGenerationJobSourceType;\n source_id: string;\n server_session_id: string | null;\n job_type: string;\n status: ObservationGenerationJobStatus;\n idempotency_key: string;\n bullmq_job_id: string | null;\n attempts: number;\n max_attempts: number;\n next_attempt_at: Date | null;\n locked_at: Date | null;\n locked_by: string | null;\n completed_at: Date | null;\n failed_at: Date | null;\n cancelled_at: Date | null;\n last_error: unknown | null;\n payload: unknown;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface JobEventRow {\n id: string;\n generation_job_id: string;\n event_type: ObservationGenerationJobEventType;\n status_after: ObservationGenerationJobStatus;\n attempt: number;\n details: unknown;\n created_at: Date;\n}\n\nexport class PostgresObservationGenerationJobRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: {\n id?: string;\n projectId: string;\n teamId: string;\n sourceType: ObservationGenerationJobSourceType;\n sourceId: string;\n agentEventId?: string | null;\n serverSessionId?: string | null;\n jobType: string;\n status?: ObservationGenerationJobStatus;\n bullmqJobId?: string | null;\n maxAttempts?: number;\n payload?: JsonObject;\n }): Promise<PostgresObservationGenerationJob> {\n await this.validateSource(input);\n const sourceModel = normalizeSourceModel(input);\n const idempotencyKey = buildObservationGenerationJobIdempotencyKey(input);\n const row = await queryOne<JobRow>(\n this.client,\n `\n INSERT INTO observation_generation_jobs (\n id, project_id, team_id, agent_event_id, source_type, source_id,\n server_session_id, job_type, status, idempotency_key, bullmq_job_id,\n max_attempts, payload\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13::jsonb)\n ON CONFLICT (idempotency_key) DO UPDATE SET\n payload = observation_generation_jobs.payload || excluded.payload,\n updated_at = now()\n RETURNING *\n `,\n [\n input.id ?? newId(),\n input.projectId,\n input.teamId,\n sourceModel.agentEventId,\n input.sourceType,\n input.sourceId,\n sourceModel.serverSessionId,\n input.jobType,\n input.status ?? 'queued',\n idempotencyKey,\n input.bullmqJobId ?? null,\n input.maxAttempts ?? 3,\n JSON.stringify(input.payload ?? {})\n ]\n );\n return mapJobRow(row!);\n }\n\n async getByIdForScope(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresObservationGenerationJob | null> {\n const row = await queryOne<JobRow>(\n this.client,\n 'SELECT * FROM observation_generation_jobs WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.id, input.projectId, input.teamId]\n );\n return row ? mapJobRow(row) : null;\n }\n\n async transitionStatus(input: {\n id: string;\n projectId: string;\n teamId: string;\n status: ObservationGenerationJobStatus;\n lockedBy?: string | null;\n lastError?: JsonObject | null;\n nextAttemptAt?: Date | null;\n }): Promise<PostgresObservationGenerationJob | null> {\n const row = await queryOne<JobRow>(\n this.client,\n `\n UPDATE observation_generation_jobs\n SET\n status = $2,\n attempts = CASE WHEN $2 = 'processing' THEN attempts + 1 ELSE attempts END,\n locked_at = CASE WHEN $2 = 'processing' THEN now() ELSE NULL END,\n locked_by = CASE WHEN $2 = 'processing' THEN $3 ELSE NULL END,\n next_attempt_at = CASE WHEN $2 = 'queued' THEN $4::timestamptz ELSE NULL::timestamptz END,\n completed_at = CASE WHEN $2 = 'completed' THEN now() ELSE NULL END,\n failed_at = CASE WHEN $2 = 'failed' THEN now() ELSE NULL END,\n cancelled_at = CASE WHEN $2 = 'cancelled' THEN now() ELSE NULL END,\n last_error = $5::jsonb,\n updated_at = now()\n WHERE id = $1\n AND project_id = $6\n AND team_id = $7\n AND (\n (status = 'queued' AND $2 IN ('processing', 'failed', 'cancelled'))\n OR\n (status = 'processing' AND $2 IN ('queued', 'completed', 'failed', 'cancelled'))\n )\n AND ($2 <> 'processing' OR attempts < max_attempts)\n AND ($2 <> 'queued' OR attempts < max_attempts)\n RETURNING *\n `,\n [\n input.id,\n input.status,\n input.lockedBy ?? null,\n input.nextAttemptAt ?? null,\n input.lastError == null ? null : JSON.stringify(input.lastError),\n input.projectId,\n input.teamId\n ]\n );\n if (row) {\n return mapJobRow(row);\n }\n\n const current = await queryOne<JobRow>(\n this.client,\n 'SELECT * FROM observation_generation_jobs WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.id, input.projectId, input.teamId]\n );\n if (!current) {\n return null;\n }\n assertValidJobStatusTransition(mapJobRow(current), input.status);\n throw new Error('observation generation job status transition was not applied');\n }\n\n async listByStatusForScope(input: {\n status: ObservationGenerationJobStatus;\n projectId: string;\n teamId: string;\n limit?: number;\n }): Promise<PostgresObservationGenerationJob[]> {\n const result = await this.client.query<JobRow>(\n `\n SELECT * FROM observation_generation_jobs\n WHERE status = $1 AND project_id = $2 AND team_id = $3\n ORDER BY created_at ASC\n LIMIT $4\n `,\n [input.status, input.projectId, input.teamId, input.limit ?? 100]\n );\n return result.rows.map(mapJobRow);\n }\n\n private async validateSource(input: {\n projectId: string;\n teamId: string;\n sourceType: ObservationGenerationJobSourceType;\n sourceId: string;\n agentEventId?: string | null;\n serverSessionId?: string | null;\n }): Promise<void> {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n if (input.sourceType === 'agent_event') {\n const eventId = input.agentEventId ?? input.sourceId;\n const row = await queryOne<{ id: string; server_session_id: string | null }>(\n this.client,\n 'SELECT id, server_session_id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [eventId, input.projectId, input.teamId]\n );\n if (!row || input.sourceId !== eventId) {\n throw new Error('agent_event source_id must belong to project_id and team_id');\n }\n if (input.serverSessionId) {\n await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);\n if (row.server_session_id && row.server_session_id !== input.serverSessionId) {\n throw new Error('server_session_id must match the agent_event server_session_id');\n }\n }\n return;\n }\n\n if (input.sourceType === 'session_summary') {\n const sessionId = input.serverSessionId ?? input.sourceId;\n await assertSessionOwnership(this.client, sessionId, input.projectId, input.teamId);\n if (input.sourceId !== sessionId) {\n throw new Error('session_summary source_id must equal server_session_id');\n }\n return;\n }\n\n const observation = await queryOne<{ id: string }>(\n this.client,\n 'SELECT id FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.sourceId, input.projectId, input.teamId]\n );\n if (!observation) {\n throw new Error('observation_reindex source_id must belong to project_id and team_id');\n }\n if (input.serverSessionId) {\n await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);\n }\n }\n}\n\nexport class PostgresObservationGenerationJobEventsRepository {\n constructor(private client: PostgresQueryable) {}\n\n async append(input: {\n id?: string;\n generationJobId: string;\n projectId: string;\n teamId: string;\n eventType: ObservationGenerationJobEventType;\n statusAfter: ObservationGenerationJobStatus;\n attempt?: number;\n details?: JsonObject;\n }): Promise<PostgresObservationGenerationJobEvent> {\n const row = await queryOne<JobEventRow>(\n this.client,\n `\n INSERT INTO observation_generation_job_events (\n id, generation_job_id, event_type, status_after, attempt, details\n )\n SELECT $1, jobs.id, $4, $5, $6, $7::jsonb\n FROM observation_generation_jobs jobs\n WHERE jobs.id = $2\n AND jobs.project_id = $3\n AND jobs.team_id = $8\n RETURNING observation_generation_job_events.*\n `,\n [\n input.id ?? newId(),\n input.generationJobId,\n input.projectId,\n input.eventType,\n input.statusAfter,\n input.attempt ?? 0,\n JSON.stringify(input.details ?? {}),\n input.teamId\n ]\n );\n if (!row) {\n throw new Error('generation_job_id must belong to project_id and team_id');\n }\n return mapJobEventRow(row!);\n }\n\n async listByJobForScope(input: {\n generationJobId: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresObservationGenerationJobEvent[]> {\n const result = await this.client.query<JobEventRow>(\n `\n SELECT events.*\n FROM observation_generation_job_events events\n INNER JOIN observation_generation_jobs jobs ON jobs.id = events.generation_job_id\n WHERE events.generation_job_id = $1 AND jobs.project_id = $2 AND jobs.team_id = $3\n ORDER BY events.created_at ASC\n `,\n [input.generationJobId, input.projectId, input.teamId]\n );\n return result.rows.map(mapJobEventRow);\n }\n}\n\nexport function buildObservationGenerationJobIdempotencyKey(input: {\n teamId: string;\n projectId: string;\n sourceType: ObservationGenerationJobSourceType;\n sourceId: string;\n jobType: string;\n}): string {\n return `observation_generation_job:v1:${deterministicKey([\n input.teamId,\n input.projectId,\n input.sourceType,\n input.sourceId,\n input.jobType\n ])}`;\n}\n\nfunction normalizeSourceModel(input: {\n sourceType: ObservationGenerationJobSourceType;\n sourceId: string;\n agentEventId?: string | null;\n serverSessionId?: string | null;\n}): { agentEventId: string | null; serverSessionId: string | null } {\n if (input.sourceType === 'agent_event') {\n return { agentEventId: input.agentEventId ?? input.sourceId, serverSessionId: input.serverSessionId ?? null };\n }\n if (input.sourceType === 'session_summary') {\n return { agentEventId: null, serverSessionId: input.serverSessionId ?? input.sourceId };\n }\n return { agentEventId: null, serverSessionId: input.serverSessionId ?? null };\n}\n\nconst TERMINAL_JOB_STATUSES = new Set<ObservationGenerationJobStatus>(['completed', 'failed', 'cancelled']);\n\nconst ALLOWED_JOB_TRANSITIONS: Record<ObservationGenerationJobStatus, readonly ObservationGenerationJobStatus[]> = {\n queued: ['processing', 'failed', 'cancelled'],\n processing: ['queued', 'completed', 'failed', 'cancelled'],\n completed: [],\n failed: [],\n cancelled: []\n};\n\nfunction assertValidJobStatusTransition(\n current: PostgresObservationGenerationJob,\n nextStatus: ObservationGenerationJobStatus\n): void {\n if (TERMINAL_JOB_STATUSES.has(current.status)) {\n throw new Error(`cannot transition observation generation job from terminal status ${current.status}`);\n }\n\n if (!ALLOWED_JOB_TRANSITIONS[current.status].includes(nextStatus)) {\n throw new Error(`illegal observation generation job transition from ${current.status} to ${nextStatus}`);\n }\n\n if (nextStatus === 'processing' && current.attempts >= current.maxAttempts) {\n throw new Error('cannot process observation generation job after max_attempts is reached');\n }\n\n if (nextStatus === 'queued' && current.attempts >= current.maxAttempts) {\n throw new Error('cannot retry observation generation job after max_attempts is reached');\n }\n}\n\nfunction mapJobRow(row: JobRow): PostgresObservationGenerationJob {\n return {\n id: row.id,\n projectId: row.project_id,\n teamId: row.team_id,\n agentEventId: row.agent_event_id,\n sourceType: row.source_type,\n sourceId: row.source_id,\n serverSessionId: row.server_session_id,\n jobType: row.job_type,\n status: row.status,\n idempotencyKey: row.idempotency_key,\n bullmqJobId: row.bullmq_job_id,\n attempts: row.attempts,\n maxAttempts: row.max_attempts,\n nextAttemptAtEpoch: toDate(row.next_attempt_at)?.getTime() ?? null,\n lockedAtEpoch: toDate(row.locked_at)?.getTime() ?? null,\n lockedBy: row.locked_by,\n completedAtEpoch: toDate(row.completed_at)?.getTime() ?? null,\n failedAtEpoch: toDate(row.failed_at)?.getTime() ?? null,\n cancelledAtEpoch: toDate(row.cancelled_at)?.getTime() ?? null,\n lastError: row.last_error == null ? null : toJsonObject(row.last_error),\n payload: toJsonObject(row.payload),\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n\nfunction mapJobEventRow(row: JobEventRow): PostgresObservationGenerationJobEvent {\n return {\n id: row.id,\n generationJobId: row.generation_job_id,\n eventType: row.event_type,\n statusAfter: row.status_after,\n attempt: row.attempt,\n details: toJsonObject(row.details),\n createdAtEpoch: toEpoch(row.created_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, JsonValue, PostgresQueryable } from './utils.js';\nimport {\n assertProjectOwnership,\n assertSessionOwnership,\n canonicalJson,\n deterministicKey,\n newId,\n queryOne,\n toEpoch,\n toJsonObject\n} from './utils.js';\nimport { normalizePlatformSourceOrNull } from '../../shared/platform-source.js';\n\nexport type ObservationSourceType = 'agent_event' | 'session_summary' | 'observation_reindex' | 'manual';\n\nexport interface PostgresObservation {\n id: string;\n projectId: string;\n teamId: string;\n serverSessionId: string | null;\n kind: string;\n content: string;\n generationKey: string | null;\n metadata: JsonObject;\n embedding: JsonValue | null;\n createdByJobId: string | null;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\nexport interface PostgresObservationSource {\n id: string;\n observationId: string;\n agentEventId: string | null;\n generationJobId: string | null;\n sourceType: ObservationSourceType;\n sourceId: string;\n metadata: JsonObject;\n createdAtEpoch: number;\n}\n\ninterface ObservationRow {\n id: string;\n project_id: string;\n team_id: string;\n server_session_id: string | null;\n kind: string;\n content: string;\n generation_key: string | null;\n metadata: unknown;\n embedding: unknown | null;\n created_by_job_id: string | null;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface ObservationSourceRow {\n id: string;\n observation_id: string;\n agent_event_id: string | null;\n generation_job_id: string | null;\n source_type: ObservationSourceType;\n source_id: string;\n metadata: unknown;\n created_at: Date;\n}\n\nexport class PostgresObservationRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: {\n id?: string;\n projectId: string;\n teamId: string;\n serverSessionId?: string | null;\n kind?: string;\n content: string;\n generationKey?: string | null;\n metadata?: JsonObject;\n embedding?: JsonValue | null;\n createdByJobId?: string | null;\n }): Promise<PostgresObservation> {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n if (input.serverSessionId) {\n await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);\n }\n if (input.createdByJobId) {\n await assertJobOwnership(this.client, input.createdByJobId, input.projectId, input.teamId);\n }\n\n const row = await queryOne<ObservationRow>(\n this.client,\n `\n INSERT INTO observations (\n id, project_id, team_id, server_session_id, kind, content,\n generation_key, metadata, embedding, created_by_job_id\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9::jsonb, $10)\n ON CONFLICT (team_id, project_id, generation_key) WHERE generation_key IS NOT NULL DO UPDATE SET\n updated_at = observations.updated_at\n RETURNING *\n `,\n [\n input.id ?? newId(),\n input.projectId,\n input.teamId,\n input.serverSessionId ?? null,\n input.kind ?? 'observation',\n input.content,\n input.generationKey ?? null,\n JSON.stringify(input.metadata ?? {}),\n input.embedding == null ? null : JSON.stringify(input.embedding),\n input.createdByJobId ?? null\n ]\n );\n return mapObservationRow(row!);\n }\n\n async getByIdForScope(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresObservation | null> {\n const row = await queryOne<ObservationRow>(\n this.client,\n 'SELECT * FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.id, input.projectId, input.teamId]\n );\n return row ? mapObservationRow(row) : null;\n }\n\n async listByProject(input: {\n projectId: string;\n teamId: string;\n serverSessionId?: string | null;\n limit?: number;\n }): Promise<PostgresObservation[]> {\n const result = await this.client.query<ObservationRow>(\n `\n SELECT * FROM observations\n WHERE project_id = $1\n AND team_id = $2\n AND ($3::text IS NULL OR server_session_id = $3)\n ORDER BY created_at DESC\n LIMIT $4\n `,\n [input.projectId, input.teamId, input.serverSessionId ?? null, input.limit ?? 100]\n );\n return result.rows.map(mapObservationRow);\n }\n\n async search(input: {\n projectId: string;\n teamId: string;\n query: string;\n limit?: number;\n platformSource?: string | null;\n }): Promise<PostgresObservation[]> {\n const platformSource = normalizePlatformSourceOrNull(input.platformSource);\n const result = await this.client.query<ObservationRow>(\n `\n SELECT observations.* FROM observations\n LEFT JOIN server_sessions\n ON server_sessions.id = observations.server_session_id\n AND server_sessions.project_id = observations.project_id\n AND server_sessions.team_id = observations.team_id\n WHERE observations.project_id = $1\n AND observations.team_id = $2\n AND observations.content_search @@ websearch_to_tsquery('english', $3)\n AND (\n $5::text IS NULL\n OR server_sessions.platform_source = $5\n OR (\n observations.server_session_id IS NULL\n AND EXISTS (\n SELECT 1\n FROM observation_sources\n INNER JOIN agent_events\n ON agent_events.id = observation_sources.agent_event_id\n AND agent_events.project_id = observations.project_id\n AND agent_events.team_id = observations.team_id\n WHERE observation_sources.observation_id = observations.id\n AND observation_sources.source_type = 'agent_event'\n AND agent_events.platform_source = $5\n )\n )\n )\n ORDER BY ts_rank(observations.content_search, websearch_to_tsquery('english', $3)) DESC, observations.updated_at DESC\n LIMIT $4\n `,\n [input.projectId, input.teamId, input.query, input.limit ?? 20, platformSource]\n );\n return result.rows.map(mapObservationRow);\n }\n}\n\nexport class PostgresObservationSourcesRepository {\n constructor(private client: PostgresQueryable) {}\n\n async addSource(input: {\n id?: string;\n observationId: string;\n projectId: string;\n teamId: string;\n sourceType: ObservationSourceType;\n sourceId: string;\n agentEventId?: string | null;\n generationJobId?: string | null;\n metadata?: JsonObject;\n }): Promise<PostgresObservationSource> {\n const observation = await queryOne<{ id: string }>(\n this.client,\n 'SELECT id FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.observationId, input.projectId, input.teamId]\n );\n if (!observation) {\n throw new Error('observation_id does not exist');\n }\n\n const agentEventId = input.sourceType === 'agent_event'\n ? input.agentEventId ?? input.sourceId\n : null;\n\n if (input.sourceType === 'agent_event') {\n if (agentEventId !== input.sourceId) {\n throw new Error('agent_event source_id must equal agent_event_id');\n }\n await assertAgentEventOwnership(this.client, input.sourceId, input.projectId, input.teamId);\n } else if (input.sourceType === 'session_summary' && !input.generationJobId) {\n await assertSessionOwnership(this.client, input.sourceId, input.projectId, input.teamId);\n } else if (input.sourceType === 'observation_reindex' && !input.generationJobId) {\n await assertObservationOwnership(this.client, input.sourceId, input.projectId, input.teamId);\n }\n if (input.generationJobId) {\n await assertGenerationJobMatchesSource(this.client, {\n generationJobId: input.generationJobId,\n projectId: input.projectId,\n teamId: input.teamId,\n sourceType: input.sourceType,\n sourceId: input.sourceId,\n agentEventId\n });\n }\n\n const row = await queryOne<ObservationSourceRow>(\n this.client,\n `\n INSERT INTO observation_sources (\n id, observation_id, agent_event_id, generation_job_id,\n source_type, source_id, metadata\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb)\n ON CONFLICT (observation_id, source_type, source_id) DO UPDATE SET\n metadata = observation_sources.metadata || excluded.metadata\n RETURNING *\n `,\n [\n input.id ?? newId(),\n input.observationId,\n agentEventId,\n input.generationJobId ?? null,\n input.sourceType,\n input.sourceId,\n JSON.stringify(input.metadata ?? {})\n ]\n );\n return mapObservationSourceRow(row!);\n }\n\n async listByObservationForScope(input: {\n observationId: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresObservationSource[]> {\n const result = await this.client.query<ObservationSourceRow>(\n `\n SELECT observation_sources.*\n FROM observation_sources\n INNER JOIN observations\n ON observations.id = observation_sources.observation_id\n WHERE observation_sources.observation_id = $1\n AND observations.project_id = $2\n AND observations.team_id = $3\n ORDER BY observation_sources.created_at ASC\n `,\n [input.observationId, input.projectId, input.teamId]\n );\n return result.rows.map(mapObservationSourceRow);\n }\n}\n\nexport function buildObservationGenerationKey(input: {\n generationJobId: string;\n parsedObservationIndex: number;\n content: string;\n}): string {\n return `generation:v1:${input.generationJobId}:${input.parsedObservationIndex}:${deterministicKey([\n canonicalJson(input.content.trim())\n ])}`;\n}\n\nasync function assertJobOwnership(\n client: PostgresQueryable,\n generationJobId: string,\n projectId: string,\n teamId: string\n): Promise<void> {\n const row = await queryOne<{ id: string }>(\n client,\n 'SELECT id FROM observation_generation_jobs WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [generationJobId, projectId, teamId]\n );\n if (!row) {\n throw new Error('generation_job_id must belong to project_id and team_id');\n }\n}\n\nasync function assertGenerationJobMatchesSource(\n client: PostgresQueryable,\n input: {\n generationJobId: string;\n projectId: string;\n teamId: string;\n sourceType: ObservationSourceType;\n sourceId: string;\n agentEventId: string | null;\n }\n): Promise<void> {\n if (input.sourceType === 'manual') {\n throw new Error('manual observation sources cannot be linked to a generation_job_id');\n }\n\n const row = await queryOne<{\n id: string;\n source_type: string;\n source_id: string;\n agent_event_id: string | null;\n }>(\n client,\n `\n SELECT id, source_type, source_id, agent_event_id\n FROM observation_generation_jobs\n WHERE id = $1 AND project_id = $2 AND team_id = $3\n `,\n [input.generationJobId, input.projectId, input.teamId]\n );\n if (!row) {\n throw new Error('generation_job_id must belong to project_id and team_id');\n }\n if (row.source_type !== input.sourceType || row.source_id !== input.sourceId) {\n throw new Error('generation_job_id source model must match observation source');\n }\n if (input.sourceType === 'agent_event' && row.agent_event_id !== input.agentEventId) {\n throw new Error('generation_job_id agent_event_id must match observation source');\n }\n}\n\nasync function assertAgentEventOwnership(\n client: PostgresQueryable,\n agentEventId: string,\n projectId: string,\n teamId: string\n): Promise<void> {\n const row = await queryOne<{ id: string }>(\n client,\n 'SELECT id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [agentEventId, projectId, teamId]\n );\n if (!row) {\n throw new Error('agent_event_id must belong to project_id and team_id');\n }\n}\n\nasync function assertObservationOwnership(\n client: PostgresQueryable,\n observationId: string,\n projectId: string,\n teamId: string\n): Promise<void> {\n const row = await queryOne<{ id: string }>(\n client,\n 'SELECT id FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [observationId, projectId, teamId]\n );\n if (!row) {\n throw new Error('observation_reindex source_id must belong to project_id and team_id');\n }\n}\n\nfunction mapObservationRow(row: ObservationRow): PostgresObservation {\n return {\n id: row.id,\n projectId: row.project_id,\n teamId: row.team_id,\n serverSessionId: row.server_session_id,\n kind: row.kind,\n content: row.content,\n generationKey: row.generation_key,\n metadata: toJsonObject(row.metadata),\n embedding: row.embedding,\n createdByJobId: row.created_by_job_id,\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n\nfunction mapObservationSourceRow(row: ObservationSourceRow): PostgresObservationSource {\n return {\n id: row.id,\n observationId: row.observation_id,\n agentEventId: row.agent_event_id,\n generationJobId: row.generation_job_id,\n sourceType: row.source_type,\n sourceId: row.source_id,\n metadata: toJsonObject(row.metadata),\n createdAtEpoch: toEpoch(row.created_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, PostgresQueryable } from './utils.js';\nimport { newId, queryOne, toEpoch, toJsonObject } from './utils.js';\n\nexport interface PostgresProject {\n id: string;\n teamId: string;\n name: string;\n metadata: JsonObject;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\ninterface ProjectRow {\n id: string;\n team_id: string;\n name: string;\n metadata: unknown;\n created_at: Date;\n updated_at: Date;\n}\n\nexport class PostgresProjectsRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: {\n id?: string;\n teamId: string;\n name: string;\n metadata?: JsonObject;\n }): Promise<PostgresProject> {\n const id = input.id ?? newId();\n const row = await queryOne<ProjectRow>(\n this.client,\n `\n INSERT INTO projects (id, team_id, name, metadata)\n VALUES ($1, $2, $3, $4::jsonb)\n RETURNING *\n `,\n [id, input.teamId, input.name, JSON.stringify(input.metadata ?? {})]\n );\n return mapProjectRow(row!);\n }\n\n async getByIdForTeam(id: string, teamId: string): Promise<PostgresProject | null> {\n const row = await queryOne<ProjectRow>(\n this.client,\n 'SELECT * FROM projects WHERE id = $1 AND team_id = $2',\n [id, teamId]\n );\n return row ? mapProjectRow(row) : null;\n }\n}\n\nfunction mapProjectRow(row: ProjectRow): PostgresProject {\n return {\n id: row.id,\n teamId: row.team_id,\n name: row.name,\n metadata: toJsonObject(row.metadata),\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JsonObject, PostgresQueryable } from './utils.js';\nimport { assertProjectOwnership, deterministicKey, newId, queryOne, toDate, toEpoch, toJsonObject } from './utils.js';\nimport type { PostgresAgentEvent } from './agent-events.js';\nimport { normalizePlatformSourceOrNull } from '../../shared/platform-source.js';\n\nexport interface PostgresServerSession {\n id: string;\n projectId: string;\n teamId: string;\n externalSessionId: string | null;\n idempotencyKey: string | null;\n contentSessionId: string | null;\n agentId: string | null;\n agentType: string | null;\n platformSource: string | null;\n generationStatus: string;\n metadata: JsonObject;\n startedAtEpoch: number;\n endedAtEpoch: number | null;\n lastGeneratedAtEpoch: number | null;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\ninterface ServerSessionRow {\n id: string;\n project_id: string;\n team_id: string;\n external_session_id: string | null;\n idempotency_key: string | null;\n content_session_id: string | null;\n agent_id: string | null;\n agent_type: string | null;\n platform_source: string | null;\n generation_status: string;\n metadata: unknown;\n started_at: Date;\n ended_at: Date | null;\n last_generated_at: Date | null;\n created_at: Date;\n updated_at: Date;\n}\n\nexport class PostgresServerSessionsRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: {\n id?: string;\n projectId: string;\n teamId: string;\n externalSessionId?: string | null;\n contentSessionId?: string | null;\n agentId?: string | null;\n agentType?: string | null;\n platformSource?: string | null;\n generationStatus?: string;\n metadata?: JsonObject;\n }): Promise<PostgresServerSession> {\n await assertProjectOwnership(this.client, input.projectId, input.teamId);\n const id = input.id ?? newId();\n const platformSource = normalizePlatformSourceOrNull(input.platformSource);\n const idempotencyKey = buildServerSessionIdempotencyKey({\n ...input,\n platformSource,\n });\n const row = await queryOne<ServerSessionRow>(\n this.client,\n `\n INSERT INTO server_sessions (\n id, project_id, team_id, external_session_id, idempotency_key, content_session_id,\n agent_id, agent_type, platform_source, generation_status, metadata\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11::jsonb)\n ON CONFLICT (project_id, idempotency_key) WHERE idempotency_key IS NOT NULL DO UPDATE SET\n external_session_id = excluded.external_session_id,\n content_session_id = excluded.content_session_id,\n agent_id = excluded.agent_id,\n agent_type = excluded.agent_type,\n platform_source = excluded.platform_source,\n generation_status = excluded.generation_status,\n metadata = excluded.metadata,\n updated_at = now()\n RETURNING *\n `,\n [\n id,\n input.projectId,\n input.teamId,\n input.externalSessionId ?? null,\n idempotencyKey,\n input.contentSessionId ?? null,\n input.agentId ?? null,\n input.agentType ?? null,\n platformSource,\n input.generationStatus ?? 'idle',\n JSON.stringify(input.metadata ?? {})\n ]\n );\n return mapServerSessionRow(row!);\n }\n\n async getByIdForScope(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresServerSession | null> {\n const row = await queryOne<ServerSessionRow>(\n this.client,\n 'SELECT * FROM server_sessions WHERE id = $1 AND project_id = $2 AND team_id = $3',\n [input.id, input.projectId, input.teamId]\n );\n return row ? mapServerSessionRow(row) : null;\n }\n\n async listByProject(projectId: string, teamId: string): Promise<PostgresServerSession[]> {\n const result = await this.client.query<ServerSessionRow>(\n `\n SELECT * FROM server_sessions\n WHERE project_id = $1 AND team_id = $2\n ORDER BY started_at DESC\n `,\n [projectId, teamId]\n );\n return result.rows.map(mapServerSessionRow);\n }\n\n async findByExternalIdForScope(input: {\n externalSessionId: string;\n projectId: string;\n teamId: string;\n platformSource?: string | null;\n }): Promise<PostgresServerSession | null> {\n const hasPlatformScope = Object.prototype.hasOwnProperty.call(input, 'platformSource');\n const platformSource = hasPlatformScope\n ? normalizePlatformSourceOrNull(input.platformSource)\n : null;\n const row = await queryOne<ServerSessionRow>(\n this.client,\n `\n SELECT * FROM server_sessions\n WHERE external_session_id = $1 AND project_id = $2 AND team_id = $3\n AND (\n $4::boolean = false\n OR ($5::text IS NULL AND platform_source IS NULL)\n OR platform_source = $5\n )\n ORDER BY started_at DESC\n LIMIT 1\n `,\n [input.externalSessionId, input.projectId, input.teamId, hasPlatformScope, platformSource]\n );\n return row ? mapServerSessionRow(row) : null;\n }\n\n // Resolve only the server_session id for a content session. Used by the\n // /v1/events ingest linkage path, which needs nothing but the id, so we select\n // a single column to keep this hot-path query light.\n async findIdByContentSessionId(input: {\n contentSessionId: string;\n projectId: string;\n teamId: string;\n platformSource?: string | null;\n }): Promise<string | null> {\n const hasPlatformScope = Object.prototype.hasOwnProperty.call(input, 'platformSource');\n const platformSource = hasPlatformScope\n ? normalizePlatformSourceOrNull(input.platformSource)\n : null;\n const row = await queryOne<{ id: string }>(\n this.client,\n `\n SELECT id FROM server_sessions\n WHERE content_session_id = $1 AND project_id = $2 AND team_id = $3\n AND (\n $4::boolean = false\n OR ($5::text IS NULL AND platform_source IS NULL)\n OR platform_source = $5\n )\n ORDER BY started_at DESC\n LIMIT 1\n `,\n [input.contentSessionId, input.projectId, input.teamId, hasPlatformScope, platformSource]\n );\n return row ? row.id : null;\n }\n\n /**\n * End a server session by setting `ended_at = now()` if not already set.\n * Idempotent: if `ended_at` is already populated, returns the row unchanged.\n * Returns null if no row matches the (id, project_id, team_id) tuple.\n */\n async endSession(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresServerSession | null> {\n const updated = await queryOne<ServerSessionRow>(\n this.client,\n `\n UPDATE server_sessions\n SET ended_at = COALESCE(ended_at, now()),\n updated_at = CASE WHEN ended_at IS NULL THEN now() ELSE updated_at END\n WHERE id = $1 AND project_id = $2 AND team_id = $3\n RETURNING *\n `,\n [input.id, input.projectId, input.teamId]\n );\n return updated ? mapServerSessionRow(updated) : null;\n }\n\n async markGenerationStarted(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresServerSession | null> {\n const updated = await queryOne<ServerSessionRow>(\n this.client,\n `\n UPDATE server_sessions\n SET generation_status = 'processing', updated_at = now()\n WHERE id = $1 AND project_id = $2 AND team_id = $3\n RETURNING *\n `,\n [input.id, input.projectId, input.teamId]\n );\n return updated ? mapServerSessionRow(updated) : null;\n }\n\n async markGenerationCompleted(input: {\n id: string;\n projectId: string;\n teamId: string;\n }): Promise<PostgresServerSession | null> {\n const updated = await queryOne<ServerSessionRow>(\n this.client,\n `\n UPDATE server_sessions\n SET generation_status = 'completed',\n last_generated_at = now(),\n updated_at = now()\n WHERE id = $1 AND project_id = $2 AND team_id = $3\n RETURNING *\n `,\n [input.id, input.projectId, input.teamId]\n );\n return updated ? mapServerSessionRow(updated) : null;\n }\n\n async markGenerationFailed(input: {\n id: string;\n projectId: string;\n teamId: string;\n error?: string | null;\n }): Promise<PostgresServerSession | null> {\n const updated = await queryOne<ServerSessionRow>(\n this.client,\n `\n UPDATE server_sessions\n SET generation_status = 'failed',\n metadata = jsonb_set(\n COALESCE(metadata, '{}'::jsonb),\n '{lastGenerationError}',\n COALESCE(to_jsonb($4::text), 'null'::jsonb),\n true\n ),\n updated_at = now()\n WHERE id = $1 AND project_id = $2 AND team_id = $3\n RETURNING *\n `,\n [input.id, input.projectId, input.teamId, input.error ?? null]\n );\n return updated ? mapServerSessionRow(updated) : null;\n }\n\n /**\n * List events tied to this server_session that do NOT yet have a completed\n * observation_generation_jobs row. Tenant-scoped: rows are filtered by\n * (project_id, team_id) before any join.\n */\n async listUnprocessedEvents(input: {\n serverSessionId: string;\n projectId: string;\n teamId: string;\n limit?: number;\n }): Promise<PostgresAgentEvent[]> {\n const limit = input.limit ?? 500;\n const result = await this.client.query<UnprocessedEventRow>(\n `\n SELECT e.*\n FROM agent_events e\n WHERE e.server_session_id = $1\n AND e.project_id = $2\n AND e.team_id = $3\n AND NOT EXISTS (\n SELECT 1 FROM observation_generation_jobs j\n WHERE j.agent_event_id = e.id\n AND j.project_id = e.project_id\n AND j.team_id = e.team_id\n AND j.source_type = 'agent_event'\n AND j.status = 'completed'\n )\n ORDER BY e.occurred_at ASC\n LIMIT $4\n `,\n [input.serverSessionId, input.projectId, input.teamId, limit]\n );\n return result.rows.map(mapUnprocessedEventRow);\n }\n}\n\ninterface UnprocessedEventRow {\n id: string;\n project_id: string;\n team_id: string;\n server_session_id: string | null;\n source_adapter: string;\n source_event_id: string | null;\n idempotency_key: string;\n event_type: string;\n platform_source: string | null;\n payload: unknown;\n metadata: unknown;\n occurred_at: Date;\n received_at: Date;\n created_at: Date;\n}\n\nfunction mapUnprocessedEventRow(row: UnprocessedEventRow): PostgresAgentEvent {\n return {\n id: row.id,\n projectId: row.project_id,\n teamId: row.team_id,\n serverSessionId: row.server_session_id,\n sourceAdapter: row.source_adapter,\n sourceEventId: row.source_event_id,\n idempotencyKey: row.idempotency_key,\n eventType: row.event_type,\n platformSource: row.platform_source,\n payload: toJsonObject(row.payload),\n metadata: toJsonObject(row.metadata),\n occurredAtEpoch: row.occurred_at.getTime(),\n receivedAtEpoch: row.received_at.getTime(),\n createdAtEpoch: row.created_at.getTime()\n };\n}\n\nexport function buildServerSessionIdempotencyKey(input: {\n projectId: string;\n teamId: string;\n externalSessionId?: string | null;\n contentSessionId?: string | null;\n agentId?: string | null;\n agentType?: string | null;\n platformSource?: string | null;\n}): string | null {\n const platformSource = normalizePlatformSourceOrNull(input.platformSource);\n\n if (input.externalSessionId) {\n const parts = [\n input.teamId,\n input.projectId,\n 'external',\n ];\n if (platformSource) {\n parts.push(platformSource);\n }\n parts.push(input.externalSessionId);\n return `server_session:v1:${deterministicKey(parts)}`;\n }\n\n if (input.contentSessionId) {\n return `server_session:v1:${deterministicKey([\n input.teamId,\n input.projectId,\n 'content',\n platformSource,\n input.agentId ?? null,\n input.contentSessionId\n ])}`;\n }\n\n if (input.agentId && platformSource) {\n return `server_session:v1:${deterministicKey([\n input.teamId,\n input.projectId,\n 'agent',\n platformSource,\n input.agentId,\n input.agentType ?? null\n ])}`;\n }\n\n return null;\n}\n\nfunction mapServerSessionRow(row: ServerSessionRow): PostgresServerSession {\n return {\n id: row.id,\n projectId: row.project_id,\n teamId: row.team_id,\n externalSessionId: row.external_session_id,\n idempotencyKey: row.idempotency_key,\n contentSessionId: row.content_session_id,\n agentId: row.agent_id,\n agentType: row.agent_type,\n platformSource: row.platform_source,\n generationStatus: row.generation_status,\n metadata: toJsonObject(row.metadata),\n startedAtEpoch: toEpoch(row.started_at),\n endedAtEpoch: toDate(row.ended_at)?.getTime() ?? null,\n lastGeneratedAtEpoch: toDate(row.last_generated_at)?.getTime() ?? null,\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { PostgresQueryable, JsonObject } from './utils.js';\nimport { newId, queryOne, toEpoch, toJsonObject } from './utils.js';\n\nexport type PostgresTeamRole = 'owner' | 'admin' | 'member' | 'viewer';\n\nexport interface PostgresTeam {\n id: string;\n name: string;\n metadata: JsonObject;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\nexport interface PostgresTeamMember {\n teamId: string;\n userId: string;\n role: PostgresTeamRole;\n metadata: JsonObject;\n createdAtEpoch: number;\n updatedAtEpoch: number;\n}\n\ninterface TeamRow {\n id: string;\n name: string;\n metadata: unknown;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface TeamMemberRow {\n team_id: string;\n user_id: string;\n role: PostgresTeamRole;\n metadata: unknown;\n created_at: Date;\n updated_at: Date;\n}\n\nexport class PostgresTeamsRepository {\n constructor(private client: PostgresQueryable) {}\n\n async create(input: { id?: string; name: string; metadata?: JsonObject }): Promise<PostgresTeam> {\n const id = input.id ?? newId();\n const row = await queryOne<TeamRow>(\n this.client,\n `\n INSERT INTO teams (id, name, metadata)\n VALUES ($1, $2, $3::jsonb)\n RETURNING *\n `,\n [id, input.name, JSON.stringify(input.metadata ?? {})]\n );\n return mapTeamRow(row!);\n }\n\n async addMember(input: {\n teamId: string;\n userId: string;\n role: PostgresTeamRole;\n metadata?: JsonObject;\n }): Promise<PostgresTeamMember> {\n const row = await queryOne<TeamMemberRow>(\n this.client,\n `\n INSERT INTO team_members (team_id, user_id, role, metadata)\n VALUES ($1, $2, $3, $4::jsonb)\n ON CONFLICT (team_id, user_id) DO UPDATE SET\n role = excluded.role,\n metadata = excluded.metadata,\n updated_at = now()\n RETURNING *\n `,\n [input.teamId, input.userId, input.role, JSON.stringify(input.metadata ?? {})]\n );\n return mapTeamMemberRow(row!);\n }\n\n async getByIdForUser(input: {\n id: string;\n userId: string;\n }): Promise<PostgresTeam | null> {\n const row = await queryOne<TeamRow>(\n this.client,\n `\n SELECT teams.*\n FROM teams\n INNER JOIN team_members ON team_members.team_id = teams.id\n WHERE teams.id = $1 AND team_members.user_id = $2\n `,\n [input.id, input.userId]\n );\n return row ? mapTeamRow(row) : null;\n }\n}\n\nfunction mapTeamRow(row: TeamRow): PostgresTeam {\n return {\n id: row.id,\n name: row.name,\n metadata: toJsonObject(row.metadata),\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n\nfunction mapTeamMemberRow(row: TeamMemberRow): PostgresTeamMember {\n return {\n teamId: row.team_id,\n userId: row.user_id,\n role: row.role,\n metadata: toJsonObject(row.metadata),\n createdAtEpoch: toEpoch(row.created_at),\n updatedAtEpoch: toEpoch(row.updated_at)\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n//\n// Usage metering store. Append-only per-team usage events, aggregated for\n// quotas (src/server/middleware/rate-limit.ts) and, later, billing. `kind` is\n// open-ended so the same table records request counts, generated observations,\n// and provider token spend without a schema change.\n\nimport type { JsonObject, PostgresQueryable } from './utils.js';\nimport { newId } from './utils.js';\n\nexport type UsageKind = 'request' | 'observation' | 'tokens_in' | 'tokens_out' | (string & {});\n\nexport class PostgresUsageRepository {\n constructor(private readonly client: PostgresQueryable) {}\n\n async record(input: {\n teamId: string;\n projectId?: string | null;\n kind: UsageKind;\n quantity?: number;\n metadata?: JsonObject;\n }): Promise<void> {\n await this.client.query(\n `INSERT INTO usage_events (id, team_id, project_id, kind, quantity, metadata)\n VALUES ($1, $2, $3, $4, $5, $6::jsonb)`,\n [\n newId(),\n input.teamId,\n input.projectId ?? null,\n input.kind,\n Math.max(0, Math.trunc(input.quantity ?? 1)),\n JSON.stringify(input.metadata ?? {}),\n ],\n );\n }\n\n /** Total quantity of one `kind` for a team since `since` — the quota read. */\n async total(input: { teamId: string; kind: UsageKind; since: Date }): Promise<number> {\n const res = await this.client.query<{ total: string }>(\n `SELECT COALESCE(SUM(quantity), 0)::bigint AS total\n FROM usage_events\n WHERE team_id = $1 AND kind = $2 AND created_at >= $3`,\n [input.teamId, input.kind, input.since],\n );\n return Number(res.rows[0]?.total ?? 0);\n }\n\n /** Per-kind totals for a team since `since` — the /v1/usage read. */\n async summarize(input: { teamId: string; since: Date }): Promise<Record<string, number>> {\n const res = await this.client.query<{ kind: string; total: string }>(\n `SELECT kind, SUM(quantity)::bigint AS total\n FROM usage_events\n WHERE team_id = $1 AND created_at >= $2\n GROUP BY kind`,\n [input.teamId, input.since],\n );\n const out: Record<string, number> = {};\n for (const row of res.rows) out[row.kind] = Number(row.total);\n return out;\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n//\n// Fixed-window rate-limit counters. One atomic UPSERT per request increments the\n// (subject, window) bucket and returns the new count, so the limiter needs a\n// single round-trip and is correct across concurrent server instances (the\n// increment happens in Postgres, not in app memory).\n\nimport type { PostgresQueryable } from './utils.js';\n\nexport interface RateLimitResult {\n allowed: boolean;\n count: number;\n limit: number;\n windowStart: Date;\n}\n\nexport class PostgresRateLimitRepository {\n constructor(private readonly client: PostgresQueryable) {}\n\n /**\n * Atomically increment the current window's counter for `subjectId` and\n * report whether it is still within `limit`.\n */\n async hit(input: { subjectId: string; windowStart: Date; limit: number }): Promise<RateLimitResult> {\n const res = await this.client.query<{ count: string }>(\n `INSERT INTO rate_limit_counters (subject_id, window_start, count)\n VALUES ($1, $2, 1)\n ON CONFLICT (subject_id, window_start)\n DO UPDATE SET count = rate_limit_counters.count + 1\n RETURNING count`,\n [input.subjectId, input.windowStart],\n );\n const count = Number(res.rows[0]?.count ?? 0);\n return { allowed: count <= input.limit, count, limit: input.limit, windowStart: input.windowStart };\n }\n\n /** Best-effort cleanup of expired windows. Call off the hot path. */\n async prune(before: Date): Promise<void> {\n await this.client.query(`DELETE FROM rate_limit_counters WHERE window_start < $1`, [before]);\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { PostgresQueryable } from './utils.js';\nimport { PostgresAgentEventsRepository } from './agent-events.js';\nimport { PostgresAuthRepository } from './auth.js';\nimport {\n PostgresObservationGenerationJobEventsRepository,\n PostgresObservationGenerationJobRepository\n} from './generation-jobs.js';\nimport { PostgresObservationRepository, PostgresObservationSourcesRepository } from './observations.js';\nimport { PostgresProjectsRepository } from './projects.js';\nimport { PostgresServerSessionsRepository } from './server-sessions.js';\nimport { PostgresTeamsRepository } from './teams.js';\nimport { PostgresUsageRepository } from './usage.js';\nimport { PostgresRateLimitRepository } from './rate-limit.js';\n\nexport * from './agent-events.js';\nexport * from './auth.js';\nexport * from './config.js';\nexport * from './data-deletion.js';\nexport * from './generation-jobs.js';\nexport * from './observations.js';\nexport * from './pool.js';\nexport * from './projects.js';\nexport * from './rate-limit.js';\nexport * from './schema.js';\nexport * from './server-sessions.js';\nexport * from './teams.js';\nexport * from './usage.js';\nexport type * from './utils.js';\n\nexport interface PostgresStorageRepositories {\n teams: PostgresTeamsRepository;\n projects: PostgresProjectsRepository;\n auth: PostgresAuthRepository;\n sessions: PostgresServerSessionsRepository;\n agentEvents: PostgresAgentEventsRepository;\n observations: PostgresObservationRepository;\n observationSources: PostgresObservationSourcesRepository;\n observationGenerationJobs: PostgresObservationGenerationJobRepository;\n observationGenerationJobEvents: PostgresObservationGenerationJobEventsRepository;\n usage: PostgresUsageRepository;\n rateLimits: PostgresRateLimitRepository;\n}\n\nexport function createPostgresStorageRepositories(client: PostgresQueryable): PostgresStorageRepositories {\n return {\n teams: new PostgresTeamsRepository(client),\n projects: new PostgresProjectsRepository(client),\n auth: new PostgresAuthRepository(client),\n sessions: new PostgresServerSessionsRepository(client),\n agentEvents: new PostgresAgentEventsRepository(client),\n observations: new PostgresObservationRepository(client),\n observationSources: new PostgresObservationSourcesRepository(client),\n observationGenerationJobs: new PostgresObservationGenerationJobRepository(client),\n observationGenerationJobEvents: new PostgresObservationGenerationJobEventsRepository(client),\n usage: new PostgresUsageRepository(client),\n rateLimits: new PostgresRateLimitRepository(client)\n };\n}\n","\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport { execFile, execSync, spawn, type ChildProcess } from 'child_process';\nimport { promisify } from 'util';\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs';\nimport { logger } from '../../utils/logger.js';\nimport { SettingsDefaultsManager } from '../../shared/SettingsDefaultsManager.js';\nimport { USER_SETTINGS_PATH, paths } from '../../shared/paths.js';\nimport { sanitizeEnv } from '../../supervisor/env-sanitizer.js';\nimport { getSupervisor } from '../../supervisor/index.js';\nimport { clearDependencyStatus, recordUvxVectorSearchUnavailable } from '../../shared/dependency-health.js';\nimport { ChromaUnavailableError } from '../worker/search/errors.js';\n\nconst execFileAsync = promisify(execFile);\n\nconst CHROMA_MCP_CLIENT_NAME = 'claude-mem-chroma';\nconst CHROMA_MCP_CLIENT_VERSION = '1.0.0';\nconst MCP_CONNECTION_TIMEOUT_MS = 30_000;\nconst DEFAULT_CHROMA_PREWARM_TIMEOUT_MS = 120_000;\nconst CHROMA_PREWARM_TIMEOUT_SETTING = 'CLAUDE_MEM_CHROMA_PREWARM_TIMEOUT_MS';\nconst CHROMA_PREWARM_TIMEOUT_BOUNDS = { min: 1, max: 600_000 } as const;\nconst CHROMA_PREWARM_REAP_TIMEOUT_MS = 1_000;\nconst RECONNECT_BACKOFF_MS = 10_000;\nconst DEFAULT_CHROMA_DATA_DIR = paths.chroma();\nconst CHROMA_SUPERVISOR_ID = 'chroma-mcp';\nconst CHROMA_OUTPUT_TAIL_MAX_CHARS = 2048;\n\nconst CHROMA_MCP_PINNED_VERSION = '0.2.6';\n\n// Override transitive dep resolutions for chroma-mcp 0.2.6 (issue #2371).\n//\n// Why onnxruntime>=1.20: the shipped all-MiniLM-L6-v2 model has pytorch-2.0\n// IR. Older onnxruntime versions can't parse it and fail every embedding\n// add with `[ONNXRuntimeError] : 7 : INVALID_PROTOBUF`. uv may otherwise\n// resolve to a too-old onnxruntime on macOS arm64 / Python 3.13 depending\n// on cache state, so we force a floor.\n//\n// Why protobuf<7: protobuf 7.x's stricter generated-file check rejects\n// opentelemetry's _pb2 stubs (generated with protoc <3.19), throwing\n// `TypeError: Descriptors cannot be created directly` at chromadb import.\n// Capping below 7 lands on protobuf 6.x which opentelemetry tolerates.\n//\n// These pins are runtime-only (uvx --with) so we don't have to fork\n// chroma-mcp upstream — they apply only to claude-mem's spawned subprocess.\nconst CHROMA_MCP_DEP_OVERRIDES: ReadonlyArray<string> = [\n 'onnxruntime>=1.20',\n 'protobuf<7',\n];\n\n// Issue #2696 (revised): chroma-mcp is now spawned by invoking uvx DIRECTLY on\n// every platform — see ChromaMcpManager.resolveUvxCommand(). The previous\n// `cmd.exe /c uvx ...` path, and the cmd.exe metacharacter-quoting helper that\n// went with it, were removed: even with the dep-override specs wrapped in double\n// quotes, Node's child_process arg-quoting for cmd.exe re-mangled the `>`/`<` in\n// `onnxruntime>=1.20` / `protobuf<7`, so cmd.exe parsed them as redirection and\n// died with \"The directory name is invalid\" in ~10ms — killing semantic search.\n\nclass ChromaMcpConnectionCancelledError extends Error {\n constructor(message = 'chroma-mcp connection cancelled during shutdown') {\n super(message);\n this.name = 'ChromaMcpConnectionCancelledError';\n }\n}\n\nexport class ChromaMcpManager {\n private static instance: ChromaMcpManager | null = null;\n private client: Client | null = null;\n private transport: StdioClientTransport | null = null;\n private connected: boolean = false;\n private lastConnectionFailureTimestamp: number = 0;\n private connecting: Promise<void> | null = null;\n private activePrewarmChild: ChildProcess | null = null;\n private connectionGeneration: number = 0;\n private intentionallyClosingTransports = new WeakSet<object>();\n private static uvxAvailabilityProbe: ((command: string, env: Record<string, string>, platform: NodeJS.Platform) => boolean) | null = null;\n\n private constructor() {}\n\n static getInstance(): ChromaMcpManager {\n if (!ChromaMcpManager.instance) {\n ChromaMcpManager.instance = new ChromaMcpManager();\n }\n return ChromaMcpManager.instance;\n }\n\n private async ensureConnected(): Promise<void> {\n if (this.connected && this.client) {\n return;\n }\n\n const timeSinceLastFailure = Date.now() - this.lastConnectionFailureTimestamp;\n if (this.lastConnectionFailureTimestamp > 0 && timeSinceLastFailure < RECONNECT_BACKOFF_MS) {\n throw new ChromaUnavailableError(`chroma-mcp connection in backoff (${Math.ceil((RECONNECT_BACKOFF_MS - timeSinceLastFailure) / 1000)}s remaining)`);\n }\n\n if (this.connecting) {\n await this.connecting;\n return;\n }\n\n this.connecting = this.connectInternal();\n try {\n await this.connecting;\n } catch (error) {\n if (error instanceof ChromaMcpConnectionCancelledError) {\n logger.debug('CHROMA_MCP', 'Connection attempt cancelled during shutdown');\n throw error;\n }\n this.lastConnectionFailureTimestamp = Date.now();\n if (error instanceof Error) {\n logger.error('CHROMA_MCP', 'Connection attempt failed', {}, error);\n } else {\n logger.error('CHROMA_MCP', 'Connection attempt failed with non-Error value', { error: String(error) });\n }\n throw error;\n } finally {\n this.connecting = null;\n }\n }\n\n private async connectInternal(): Promise<void> {\n const connectionGeneration = this.connectionGeneration;\n\n // Singleton invariant (#2313): kill any pre-existing chroma-mcp subprocess\n // tree before spawning a new one. The MCP SDK's transport.close() only\n // signals the direct child (uvx); on Linux the grandchildren (uv, python,\n // chroma-mcp) get re-parented to init and survive, accumulating 20+\n // instances per session if reconnects fire repeatedly. Reuse the same\n // tree-kill primitive used by stop() so reconnect can never leave\n // orphans behind.\n await this.disposeCurrentSubprocess();\n this.assertConnectionNotCancelled(connectionGeneration);\n\n const commandArgs = this.buildCommandArgs();\n const uvxPreflightEnv = ChromaMcpManager.getUvxPreflightEnv();\n getSupervisor().assertCanSpawn('chroma mcp');\n\n // Spawn uvx DIRECTLY (no `cmd.exe /c` shell). On Windows, routing through\n // cmd.exe makes it parse the `>`/`<` in the dep-override specs as shell\n // redirection before uvx sees them; a shell-less spawn passes them literally.\n // resolveUvxCommand returns the absolute uvx.exe path on Windows (Node won't\n // PATHEXT-resolve a bare `uvx`) and bare `uvx` elsewhere (#2696).\n const uvxSpawnCommand = ChromaMcpManager.resolveUvxCommand();\n const uvxSpawnArgs = commandArgs;\n\n if (!ChromaMcpManager.isUvxAvailable(uvxSpawnCommand, uvxPreflightEnv, process.platform)) {\n const message = `uvx executable not found for chroma-mcp (${uvxSpawnCommand})`;\n recordUvxVectorSearchUnavailable(message);\n throw new ChromaUnavailableError(message);\n }\n\n const spawnEnvironment = this.getSpawnEnv(uvxPreflightEnv);\n\n await this.prewarmChromaMcp(uvxSpawnCommand, uvxSpawnArgs, spawnEnvironment, connectionGeneration);\n this.assertConnectionNotCancelled(connectionGeneration);\n\n clearDependencyStatus('uvx');\n\n logger.info('CHROMA_MCP', 'Connecting to chroma-mcp via MCP stdio', {\n command: uvxSpawnCommand,\n args: uvxSpawnArgs.join(' ')\n });\n\n this.transport = new StdioClientTransport({\n command: uvxSpawnCommand,\n args: uvxSpawnArgs,\n env: spawnEnvironment,\n cwd: os.homedir(),\n stderr: 'pipe'\n });\n const transportStderrTail = ChromaMcpManager.captureOutputTail(this.transport.stderr);\n\n this.client = new Client(\n { name: CHROMA_MCP_CLIENT_NAME, version: CHROMA_MCP_CLIENT_VERSION },\n { capabilities: {} }\n );\n\n const mcpConnectionPromise = this.client.connect(this.transport);\n let timeoutId: ReturnType<typeof setTimeout>;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new Error(`MCP connection to chroma-mcp timed out after ${MCP_CONNECTION_TIMEOUT_MS}ms`)),\n MCP_CONNECTION_TIMEOUT_MS\n );\n });\n\n try {\n await Promise.race([mcpConnectionPromise, timeoutPromise]);\n this.assertConnectionNotCancelled(connectionGeneration);\n } catch (connectionError) {\n clearTimeout(timeoutId!);\n if (\n connectionError instanceof ChromaMcpConnectionCancelledError ||\n this.connectionGeneration !== connectionGeneration\n ) {\n logger.debug('CHROMA_MCP', 'MCP connection cancelled during shutdown');\n await this.disposeCurrentSubprocess();\n throw connectionError instanceof ChromaMcpConnectionCancelledError\n ? connectionError\n : new ChromaMcpConnectionCancelledError();\n }\n const stderrTail = transportStderrTail();\n logger.warn('CHROMA_MCP', 'Connection failed, killing subprocess tree to prevent zombie', {\n error: connectionError instanceof Error ? connectionError.message : String(connectionError),\n ...(stderrTail ? { stderrTail } : {})\n });\n // Tree-kill (not just transport.close) so failed-connect descendants\n // can't survive on Linux (#2313).\n await this.disposeCurrentSubprocess();\n throw connectionError;\n }\n clearTimeout(timeoutId!);\n\n this.connected = true;\n this.registerManagedProcess();\n\n logger.info('CHROMA_MCP', 'Connected to chroma-mcp successfully');\n\n const currentTransport = this.transport;\n const currentTrackedPid = (this.transport as unknown as { _process?: ChildProcess })._process?.pid;\n this.transport.onclose = () => {\n if (this.transport !== currentTransport) {\n logger.debug('CHROMA_MCP', 'Ignoring stale onclose from previous transport');\n return;\n }\n if (\n this.connectionGeneration !== connectionGeneration ||\n this.intentionallyClosingTransports.has(currentTransport as unknown as object)\n ) {\n logger.debug('CHROMA_MCP', 'Ignoring onclose from intentionally closed transport');\n return;\n }\n logger.warn('CHROMA_MCP', 'chroma-mcp subprocess closed unexpectedly, applying reconnect backoff');\n this.connected = false;\n getSupervisor().unregisterProcess(CHROMA_SUPERVISOR_ID);\n this.client = null;\n this.transport = null;\n this.lastConnectionFailureTimestamp = Date.now();\n\n // Direct child (uvx) emitted close, but on Linux the grandchildren\n // (uv/python/chroma-mcp) often outlive their parent because MCP SDK\n // does not use process groups. Sweep the descendant tree using the\n // captured PID — best-effort; pgrep returns nothing if everything\n // already exited (#2313).\n if (currentTrackedPid) {\n ChromaMcpManager.killProcessTree(currentTrackedPid).catch((error) => {\n logger.debug('CHROMA_MCP', 'Background tree-kill after onclose finished (best-effort)', {\n pid: currentTrackedPid,\n error: error instanceof Error ? error.message : String(error)\n });\n });\n }\n };\n }\n\n private assertConnectionNotCancelled(connectionGeneration: number): void {\n if (this.connectionGeneration !== connectionGeneration) {\n throw new ChromaMcpConnectionCancelledError();\n }\n }\n\n private buildCommandArgs(): string[] {\n const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);\n const chromaMode = settings.CLAUDE_MEM_CHROMA_MODE || 'local';\n const pythonVersion = process.env.CLAUDE_MEM_PYTHON_VERSION || settings.CLAUDE_MEM_PYTHON_VERSION || '3.13';\n const launcherPrefix = ChromaMcpManager.buildLauncherPrefix(pythonVersion);\n\n if (chromaMode === 'remote') {\n const chromaHost = settings.CLAUDE_MEM_CHROMA_HOST || '127.0.0.1';\n const chromaPort = settings.CLAUDE_MEM_CHROMA_PORT || '8000';\n const chromaSsl = settings.CLAUDE_MEM_CHROMA_SSL === 'true';\n const chromaTenant = settings.CLAUDE_MEM_CHROMA_TENANT || 'default_tenant';\n const chromaDatabase = settings.CLAUDE_MEM_CHROMA_DATABASE || 'default_database';\n const chromaApiKey = settings.CLAUDE_MEM_CHROMA_API_KEY || '';\n\n const args = [\n ...launcherPrefix,\n '--client-type', 'http',\n '--host', chromaHost,\n '--port', chromaPort\n ];\n\n args.push('--ssl', chromaSsl ? 'true' : 'false');\n\n if (chromaTenant !== 'default_tenant') {\n args.push('--tenant', chromaTenant);\n }\n\n if (chromaDatabase !== 'default_database') {\n args.push('--database', chromaDatabase);\n }\n\n if (chromaApiKey) {\n args.push('--api-key', chromaApiKey);\n }\n\n return args;\n }\n\n return [\n ...launcherPrefix,\n '--client-type', 'persistent',\n '--data-dir', DEFAULT_CHROMA_DATA_DIR.replace(/\\\\/g, '/')\n ];\n }\n\n private static buildLauncherPrefix(pythonVersion: string): string[] {\n const depOverrideFlags = CHROMA_MCP_DEP_OVERRIDES.flatMap(spec => ['--with', spec]);\n return [\n '--python', pythonVersion,\n ...depOverrideFlags,\n '--from', `chroma-mcp==${CHROMA_MCP_PINNED_VERSION}`,\n 'chroma-mcp',\n ];\n }\n\n private static buildPrewarmCommandArgs(commandArgs: string[]): string[] {\n const executableIndex = commandArgs.indexOf('chroma-mcp');\n const launcherPrefix = executableIndex >= 0\n ? commandArgs.slice(0, executableIndex + 1)\n : commandArgs;\n return [...launcherPrefix, '--help'];\n }\n\n private static parseBoundedTimeoutMs(rawValue: string | undefined): number | null {\n if (!rawValue) {\n return null;\n }\n const parsed = Number.parseInt(rawValue, 10);\n if (\n Number.isFinite(parsed) &&\n parsed >= CHROMA_PREWARM_TIMEOUT_BOUNDS.min &&\n parsed <= CHROMA_PREWARM_TIMEOUT_BOUNDS.max\n ) {\n return parsed;\n }\n return null;\n }\n\n private static getChromaPrewarmTimeoutMs(): number {\n const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);\n const envValue = process.env[CHROMA_PREWARM_TIMEOUT_SETTING];\n const settingsValue = settings[CHROMA_PREWARM_TIMEOUT_SETTING];\n\n const parsed = ChromaMcpManager.parseBoundedTimeoutMs(envValue ?? settingsValue);\n if (parsed !== null) {\n return parsed;\n }\n\n if (envValue !== undefined || settingsValue) {\n logger.warn('CHROMA_MCP', `Invalid ${CHROMA_PREWARM_TIMEOUT_SETTING}, using default`, {\n value: envValue ?? settingsValue,\n min: CHROMA_PREWARM_TIMEOUT_BOUNDS.min,\n max: CHROMA_PREWARM_TIMEOUT_BOUNDS.max\n });\n }\n return DEFAULT_CHROMA_PREWARM_TIMEOUT_MS;\n }\n\n private static captureOutputTail(\n stream: { on(event: 'data', listener: (chunk: Buffer | string | Uint8Array) => void): unknown } | null | undefined,\n ): () => string {\n let tail = '';\n stream?.on('data', (chunk: Buffer | string | Uint8Array) => {\n const text = Buffer.isBuffer(chunk)\n ? chunk.toString()\n : chunk instanceof Uint8Array\n ? Buffer.from(chunk).toString()\n : String(chunk);\n tail = (tail + text).slice(-CHROMA_OUTPUT_TAIL_MAX_CHARS);\n });\n return () => tail.trim();\n }\n\n private async prewarmChromaMcp(\n command: string,\n commandArgs: string[],\n env: Record<string, string>,\n connectionGeneration: number,\n ): Promise<void> {\n this.assertConnectionNotCancelled(connectionGeneration);\n\n const args = ChromaMcpManager.buildPrewarmCommandArgs(commandArgs);\n const timeoutMs = ChromaMcpManager.getChromaPrewarmTimeoutMs();\n\n logger.info('CHROMA_MCP', 'Prewarming chroma-mcp uvx environment', {\n command,\n args: args.join(' '),\n timeoutMs\n });\n\n const child = spawn(command, args, {\n cwd: os.homedir(),\n env,\n shell: false,\n stdio: ['ignore', 'pipe', 'pipe'],\n windowsHide: process.platform === 'win32',\n });\n this.activePrewarmChild = child;\n\n const stdoutTail = ChromaMcpManager.captureOutputTail(child.stdout);\n const stderrTail = ChromaMcpManager.captureOutputTail(child.stderr);\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n const exitPromise = new Promise<void>((resolve, reject) => {\n child.once('error', (error) => {\n if (this.connectionGeneration !== connectionGeneration) {\n reject(new ChromaMcpConnectionCancelledError());\n return;\n }\n reject(error);\n });\n child.once('close', (code: number | null, signal: NodeJS.Signals | null) => {\n if (this.connectionGeneration !== connectionGeneration) {\n reject(new ChromaMcpConnectionCancelledError());\n return;\n }\n if (code === 0) {\n resolve();\n return;\n }\n reject(new Error(`chroma-mcp prewarm exited with code ${code ?? 'null'}${signal ? ` signal ${signal}` : ''}`));\n });\n });\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new Error(`chroma-mcp prewarm timed out after ${timeoutMs}ms`)),\n timeoutMs\n );\n });\n\n try {\n await Promise.race([exitPromise, timeoutPromise]);\n this.assertConnectionNotCancelled(connectionGeneration);\n logger.debug('CHROMA_MCP', 'chroma-mcp uvx prewarm completed');\n } catch (error) {\n if (error instanceof ChromaMcpConnectionCancelledError) {\n logger.debug('CHROMA_MCP', 'chroma-mcp uvx prewarm cancelled during shutdown');\n throw error;\n }\n this.assertConnectionNotCancelled(connectionGeneration);\n const errorMessage = error instanceof Error ? error.message : String(error);\n const pid = child.pid;\n const stdout = stdoutTail();\n const stderr = stderrTail();\n logger.warn('CHROMA_MCP', 'chroma-mcp uvx prewarm failed', {\n command,\n args: args.join(' '),\n timeoutMs,\n ...(pid ? { pid } : {}),\n error: errorMessage,\n ...(stdout ? { stdoutTail: stdout } : {}),\n ...(stderr ? { stderrTail: stderr } : {})\n });\n\n if (pid) {\n try {\n await ChromaMcpManager.killProcessTree(pid);\n } catch (killError) {\n logger.debug('CHROMA_MCP', 'prewarm process tree kill finished (best-effort)', {\n pid,\n error: killError instanceof Error ? killError.message : String(killError)\n });\n }\n } else {\n try { child.kill('SIGKILL'); } catch { /* already dead */ }\n }\n\n const unavailableMessage = `chroma-mcp prewarm failed: ${errorMessage}`;\n recordUvxVectorSearchUnavailable(unavailableMessage);\n throw new ChromaUnavailableError(unavailableMessage, error instanceof Error ? error : undefined);\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n if (this.activePrewarmChild === child) {\n this.activePrewarmChild = null;\n }\n }\n }\n\n async callTool(toolName: string, toolArguments: Record<string, unknown>): Promise<unknown> {\n await this.ensureConnected();\n\n logger.debug('CHROMA_MCP', `Calling tool: ${toolName}`, {\n arguments: JSON.stringify(toolArguments).slice(0, 200)\n });\n\n let result;\n try {\n result = await this.client!.callTool({\n name: toolName,\n arguments: toolArguments\n });\n } catch (transportError) {\n logger.warn('CHROMA_MCP', `Transport error during \"${toolName}\", reconnecting and retrying once`, {\n error: transportError instanceof Error ? transportError.message : String(transportError)\n });\n\n // Tree-kill the dying subprocess before reconnect. Previously this path\n // just nulled the handle, which on Linux leaks the uv/python/chroma-mcp\n // descendants every time a transport error happens (#2313).\n await this.disposeCurrentSubprocess();\n\n try {\n await this.ensureConnected();\n result = await this.client!.callTool({\n name: toolName,\n arguments: toolArguments\n });\n } catch (retryError) {\n this.connected = false;\n throw new Error(`chroma-mcp transport error during \"${toolName}\" (retry failed): ${retryError instanceof Error ? retryError.message : String(retryError)}`);\n }\n }\n\n if (result.isError) {\n const errorText = (result.content as Array<{ type: string; text?: string }>)\n ?.find(item => item.type === 'text')?.text || 'Unknown chroma-mcp error';\n throw new Error(`chroma-mcp tool \"${toolName}\" returned error: ${errorText}`);\n }\n\n const contentArray = result.content as Array<{ type: string; text?: string }>;\n if (!contentArray || contentArray.length === 0) {\n return null;\n }\n\n const firstTextContent = contentArray.find(item => item.type === 'text' && item.text);\n if (!firstTextContent || !firstTextContent.text) {\n return null;\n }\n\n try {\n return JSON.parse(firstTextContent.text);\n } catch (parseError: unknown) {\n if (parseError instanceof Error) {\n logger.debug('CHROMA_MCP', 'Non-JSON response from tool, returning null', {\n toolName,\n textPreview: firstTextContent.text.slice(0, 100)\n });\n }\n return null;\n }\n }\n\n async isHealthy(): Promise<boolean> {\n try {\n await this.callTool('chroma_list_collections', { limit: 1 });\n return true;\n } catch (error) {\n logger.warn('CHROMA_MCP', 'Health check failed', {\n error: error instanceof Error ? error.message : String(error)\n });\n return false;\n }\n }\n\n async probeSemanticSearch(): Promise<{\n ok: boolean;\n stage: 'connect' | 'list' | 'query' | 'done';\n error?: string;\n collections?: number;\n queryLatencyMs?: number;\n }> {\n let collections: number | undefined;\n\n try {\n const listResult: any = await this.callTool('chroma_list_collections', { limit: 100 });\n if (Array.isArray(listResult)) {\n collections = listResult.length;\n } else if (listResult && Array.isArray(listResult.collections)) {\n collections = listResult.collections.length;\n } else if (listResult && typeof listResult === 'object' && 'length' in listResult) {\n collections = (listResult as { length: number }).length;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.warn('CHROMA_MCP', 'Deep probe failed at list stage', { error: message });\n return { ok: false, stage: 'list', error: message };\n }\n\n const queryStartedAt = Date.now();\n try {\n await this.callTool('chroma_query_documents', {\n collection_name: 'cm__claude-mem',\n query_texts: ['ping'],\n n_results: 1\n });\n const queryLatencyMs = Date.now() - queryStartedAt;\n return { ok: true, stage: 'done', collections, queryLatencyMs };\n } catch (error) {\n const queryLatencyMs = Date.now() - queryStartedAt;\n const rawMessage = error instanceof Error ? error.message : String(error);\n const isMissingOrEmpty = /not exist|missing|empty|no such/i.test(rawMessage);\n const errorMessage = isMissingOrEmpty\n ? `collection cm__claude-mem missing or empty (${rawMessage})`\n : rawMessage;\n logger.warn('CHROMA_MCP', 'Deep probe failed at query stage', {\n error: rawMessage,\n queryLatencyMs\n });\n return {\n ok: false,\n stage: 'query',\n error: errorMessage,\n collections,\n queryLatencyMs\n };\n }\n }\n\n /**\n * Singleton enforcement helper (#2313): tree-kill the currently tracked\n * chroma-mcp subprocess and reset all state so the next spawn starts clean.\n *\n * Why this is the singleton invariant: every code path that intends to\n * abandon `this.transport` / `this.client` (reconnect, transport error,\n * connect-timeout, onclose, stop()) MUST funnel through here. The MCP\n * SDK's transport.close() only signals the direct child (uvx); on Linux\n * the grandchildren (uv, python, chroma-mcp) re-parent to init and\n * accumulate. Calling killProcessTree() against the captured PID before\n * we drop the reference is the only way to guarantee at most one\n * chroma-mcp subprocess tree exists per worker process.\n *\n * Idempotent and best-effort — safe to call when there is no active\n * subprocess (no-op in that case).\n */\n private async disposeCurrentSubprocess(): Promise<void> {\n await this.disposeActivePrewarm();\n\n const closingTransport = this.transport;\n if (closingTransport) {\n this.intentionallyClosingTransports.add(closingTransport as unknown as object);\n }\n\n const chromaProcess = (this.transport as unknown as { _process?: ChildProcess })?._process;\n const trackedPid = chromaProcess?.pid;\n\n if (trackedPid) {\n try {\n await ChromaMcpManager.killProcessTree(trackedPid);\n } catch (error) {\n logger.warn('CHROMA_MCP', 'failed to kill prior chroma-mcp tree (best-effort)', {\n pid: trackedPid,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n }\n\n if (closingTransport) {\n try { await closingTransport.close(); } catch { /* already dead */ }\n }\n if (this.client) {\n try { await this.client.close(); } catch { /* already dead */ }\n }\n\n if (trackedPid) {\n getSupervisor().unregisterProcess(CHROMA_SUPERVISOR_ID);\n }\n\n this.client = null;\n this.transport = null;\n this.connected = false;\n }\n\n private async disposeActivePrewarm(): Promise<void> {\n const prewarmChild = this.activePrewarmChild;\n if (!prewarmChild) {\n return;\n }\n if (this.activePrewarmChild === prewarmChild) {\n this.activePrewarmChild = null;\n }\n\n const pid = prewarmChild.pid;\n if (pid) {\n try {\n await ChromaMcpManager.killProcessTree(pid);\n } catch (error) {\n logger.warn('CHROMA_MCP', 'failed to kill in-flight chroma-mcp prewarm tree (best-effort)', {\n pid,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n }\n\n try {\n prewarmChild.kill('SIGKILL');\n } catch {\n // Already dead.\n }\n\n await ChromaMcpManager.waitForChildClose(prewarmChild, CHROMA_PREWARM_REAP_TIMEOUT_MS);\n }\n\n private static async waitForChildClose(child: ChildProcess, timeoutMs: number): Promise<void> {\n if (child.exitCode !== null || child.signalCode !== null) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n const finish = () => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n child.off('close', finish);\n child.off('exit', finish);\n resolve();\n };\n\n timeoutId = setTimeout(finish, timeoutMs);\n child.once('close', finish);\n child.once('exit', finish);\n });\n }\n\n /**\n * Gracefully stop the MCP connection and kill the chroma-mcp subprocess tree.\n *\n * The MCP SDK's client.close() sends stdin close -> SIGTERM -> SIGKILL to the\n * direct child (uvx), but the spawn chain (uvx -> uv -> python -> chroma-mcp)\n * can leave descendants orphaned because MCP SDK does not use process groups.\n *\n * Fix: kill the entire process tree rooted at the direct child PID BEFORE\n * closing the MCP client, ensuring no orphan python/chroma-mcp processes\n * accumulate across reconnects or worker restarts. Matches the tree-kill\n * pattern from shutdown.ts (Principle 5: OS-supervised teardown).\n */\n async stop(): Promise<void> {\n this.connectionGeneration += 1;\n\n if (!this.client && !this.transport && !this.activePrewarmChild) {\n logger.debug('CHROMA_MCP', 'No active MCP connection to stop');\n this.connecting = null;\n return;\n }\n\n logger.info('CHROMA_MCP', 'Stopping chroma-mcp MCP connection');\n\n await this.disposeCurrentSubprocess();\n this.connecting = null;\n\n logger.info('CHROMA_MCP', 'chroma-mcp MCP connection stopped');\n }\n\n /**\n * Kill a process and all its descendants (tree-kill).\n *\n * POSIX: Sends SIGTERM to the process, then uses `pkill -P` to signal\n * children recursively. Falls back to single-PID kill if pkill is unavailable.\n *\n * Windows: Uses `taskkill /T /F /PID` for full subtree teardown (same\n * pattern as shutdown.ts).\n *\n * Best-effort — swallows ESRCH (already dead) and logs other errors.\n */\n private static async killProcessTree(pid: number): Promise<void> {\n logger.debug('CHROMA_MCP', `Killing process tree rooted at PID ${pid}`);\n\n if (process.platform === 'win32') {\n try {\n await execFileAsync('taskkill', ['/PID', String(pid), '/T', '/F'], {\n timeout: 5_000,\n windowsHide: true\n });\n } catch (error) {\n // taskkill exits non-zero when the process is already dead — that's fine.\n logger.debug('CHROMA_MCP', `taskkill tree-kill finished (may already be dead)`, {\n pid,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n return;\n }\n\n // POSIX: walk descendants recursively (bottom-up) and signal each.\n // `pkill -P <pid>` only reaches direct children, so `python` /\n // `chroma-mcp` under `uv` (grandchildren) get re-parented to init and\n // survive. We collect the full descendant set via `pgrep -P` walks before\n // signaling, so the SIGTERM phase reaches every layer\n // (CodeRabbit review on PR #2282).\n try {\n const descendantsBeforeTerm = await ChromaMcpManager.collectDescendantPids(pid);\n // Signal leaves first, then the root.\n for (const child of descendantsBeforeTerm) {\n try {\n process.kill(child, 'SIGTERM');\n } catch {\n // Already gone — fine.\n }\n }\n try {\n process.kill(pid, 'SIGTERM');\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== 'ESRCH') {\n logger.debug('CHROMA_MCP', `Failed to SIGTERM PID ${pid}`, { code });\n }\n }\n\n // Brief wait for SIGTERM to propagate, then SIGKILL stragglers.\n await new Promise(resolve => setTimeout(resolve, 500));\n\n // Re-collect descendants — some layers may have re-parented during the\n // SIGTERM grace window.\n //\n // SIGKILL targets the UNION of pre-TERM and post-wait descendant sets:\n // when the root exits between snapshots, children get re-parented to\n // init and drop out of `pgrep -P <root>`. Without the union, those\n // re-parented descendants would never receive SIGKILL even though they\n // were definitely children before SIGTERM (CodeRabbit review on PR\n // #2282). Dedupe via Set since `descendantsBeforeKill` typically\n // overlaps with `descendantsBeforeTerm`.\n const descendantsBeforeKill = await ChromaMcpManager.collectDescendantPids(pid);\n const killTargets = Array.from(new Set([...descendantsBeforeTerm, ...descendantsBeforeKill]));\n for (const child of killTargets) {\n try {\n process.kill(child, 'SIGKILL');\n } catch {\n // Already dead — fine.\n }\n }\n try {\n process.kill(pid, 'SIGKILL');\n } catch {\n // Already dead — fine.\n }\n } catch (error) {\n logger.debug('CHROMA_MCP', `Process tree kill completed (best-effort)`, {\n pid,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n }\n\n /**\n * Recursively collect all descendant PIDs of `rootPid` using `pgrep -P`.\n * Returned bottom-up (leaves first) so callers can signal leaves before\n * their ancestors. Best-effort: missing pgrep / non-zero exits return [].\n */\n private static async collectDescendantPids(rootPid: number): Promise<number[]> {\n const seen = new Set<number>();\n const collected: number[] = [];\n\n async function walk(pid: number): Promise<void> {\n let stdout = '';\n try {\n const result = await execFileAsync('pgrep', ['-P', String(pid)], { timeout: 2_000 });\n stdout = result.stdout;\n } catch {\n // pgrep exits 1 when no children match — that's fine, just return.\n return;\n }\n const children = stdout\n .split('\\n')\n .map(line => line.trim())\n .filter(line => line.length > 0)\n .map(line => Number.parseInt(line, 10))\n .filter(n => Number.isFinite(n) && n > 0 && !seen.has(n));\n\n for (const child of children) {\n seen.add(child);\n await walk(child);\n // Bottom-up: push after recursion so leaves come first.\n collected.push(child);\n }\n }\n\n await walk(rootPid);\n return collected;\n }\n\n /**\n * Reset the singleton instance (for testing).\n * Awaits stop() to prevent dual subprocesses.\n */\n static async reset(): Promise<void> {\n if (ChromaMcpManager.instance) {\n await ChromaMcpManager.instance.stop();\n }\n ChromaMcpManager.instance = null;\n }\n\n private getCombinedCertPath(): string | undefined {\n const combinedCertPath = paths.combinedCerts();\n\n if (fs.existsSync(combinedCertPath)) {\n const stats = fs.statSync(combinedCertPath);\n const ageMs = Date.now() - stats.mtimeMs;\n if (ageMs < 24 * 60 * 60 * 1000) {\n return combinedCertPath;\n }\n }\n\n if (process.platform !== 'darwin') {\n return undefined;\n }\n\n try {\n let certifiPath: string | undefined;\n try {\n certifiPath = execSync(\n 'uvx --with certifi python -c \"import certifi; print(certifi.where())\"',\n { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000 }\n ).trim();\n } catch (error) {\n logger.debug('CHROMA_MCP', 'Failed to resolve certifi path via uvx', {\n error: error instanceof Error ? error.message : String(error)\n });\n return undefined;\n }\n\n if (!certifiPath || !fs.existsSync(certifiPath)) {\n return undefined;\n }\n\n let zscalerCert = '';\n try {\n zscalerCert = execSync(\n 'security find-certificate -a -c \"Zscaler\" -p /Library/Keychains/System.keychain',\n { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 5000 }\n );\n } catch (error) {\n logger.debug('CHROMA_MCP', 'No Zscaler certificate found in system keychain', {\n error: error instanceof Error ? error.message : String(error)\n });\n return undefined;\n }\n\n if (!zscalerCert ||\n !zscalerCert.includes('-----BEGIN CERTIFICATE-----') ||\n !zscalerCert.includes('-----END CERTIFICATE-----')) {\n return undefined;\n }\n\n const certifiContent = fs.readFileSync(certifiPath, 'utf8');\n const tempPath = combinedCertPath + '.tmp';\n fs.writeFileSync(tempPath, certifiContent + '\\n' + zscalerCert);\n fs.renameSync(tempPath, combinedCertPath);\n\n logger.info('CHROMA_MCP', 'Created combined SSL certificate bundle for Zscaler', {\n path: combinedCertPath\n });\n\n return combinedCertPath;\n } catch (error) {\n logger.debug('CHROMA_MCP', 'Could not create combined cert bundle', {}, error as Error);\n return undefined;\n }\n }\n\n /**\n * uv installs `uvx` to a per-user bin dir that is often NOT on the PATH the\n * worker inherited (the worker is spawned by the host with a minimal env that\n * predates the user adding uv to PATH). Without it, `uvx`/`cmd /c uvx` dies\n * with \"not recognized\" in ~25ms and semantic search silently falls back to\n * keyword (#2790). Prepend uv's known bin dirs (and an explicit override) so\n * the chroma child can always resolve uvx. Additive and cross-platform — only\n * dirs that exist are added.\n */\n private static uvBinDirs(): string[] {\n const home = os.homedir();\n const dirs = [\n process.env.CLAUDE_MEM_CHROMA_UVX_PATH, // explicit override (dir or uvx path)\n path.join(home, '.local', 'bin'), // uv default (Windows + Unix)\n path.join(home, '.cargo', 'bin'), // cargo-installed uv\n ].filter((d): d is string => Boolean(d));\n // If the override points at the uvx binary itself, use its directory.\n return dirs.map(d => {\n try {\n return fs.existsSync(d) && fs.statSync(d).isFile() ? path.dirname(d) : d;\n } catch {\n return d;\n }\n });\n }\n\n /**\n * Resolve the command used to launch uvx.\n *\n * On Windows we MUST spawn uvx.exe DIRECTLY rather than via `cmd.exe /c uvx`:\n * cmd.exe parses the `>`/`<` in the dep-override specs (onnxruntime>=1.20,\n * protobuf<7) as shell redirection before uvx sees them, and Node's\n * child_process arg-quoting for cmd.exe re-mangles even pre-quoted args, so\n * cmd.exe dies with \"The directory name is invalid\" in ~10ms. The MCP\n * transport then reports \"Connection closed\", the manager backs off, and\n * semantic search silently degrades to keyword-only (#2696 follow-up).\n *\n * Node's shell-less spawn won't resolve a bare `uvx` via PATHEXT on Windows,\n * so we resolve the absolute path to uvx.exe from the same uv bin dirs that\n * ensureUvOnPath() adds to the child PATH (honouring CLAUDE_MEM_CHROMA_UVX_PATH\n * when it points straight at a binary), falling back to bare 'uvx.exe'.\n */\n static resolveUvxCommand(platform: NodeJS.Platform = process.platform): string {\n if (platform !== 'win32') {\n return 'uvx';\n }\n const override = process.env.CLAUDE_MEM_CHROMA_UVX_PATH;\n if (override) {\n try {\n if (fs.existsSync(override) && fs.statSync(override).isFile()) {\n return override;\n }\n } catch {\n // fall through to scanning the known uv bin dirs\n }\n }\n for (const dir of ChromaMcpManager.uvBinDirs()) {\n const candidate = path.join(dir, 'uvx.exe');\n try {\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n } catch {\n // ignore and try the next candidate\n }\n }\n return 'uvx.exe';\n }\n\n private static isUvxAvailable(\n command: string,\n env: Record<string, string>,\n platform: NodeJS.Platform,\n ): boolean {\n if (ChromaMcpManager.uvxAvailabilityProbe) {\n return ChromaMcpManager.uvxAvailabilityProbe(command, env, platform);\n }\n\n const executableNames = platform === 'win32' && !command.toLowerCase().endsWith('.exe')\n ? [command, `${command}.exe`]\n : [command];\n\n if (command.includes('/') || command.includes('\\\\')) {\n return executableNames.some(candidate => {\n try {\n return fs.existsSync(candidate) && fs.statSync(candidate).isFile();\n } catch {\n return false;\n }\n });\n }\n\n const sep = platform === 'win32' ? ';' : ':';\n const pathKey = Object.keys(env).find(k => k.toLowerCase() === 'path') ?? 'PATH';\n const dirs = (env[pathKey] ?? '').split(sep).filter(Boolean);\n\n for (const dir of dirs) {\n for (const name of executableNames) {\n const candidate = path.join(dir, name);\n try {\n if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {\n return true;\n }\n } catch {\n // Try the next PATH entry.\n }\n }\n }\n return false;\n }\n\n static setUvxAvailabilityProbeForTesting(\n probe: ((command: string, env: Record<string, string>, platform: NodeJS.Platform) => boolean) | null,\n ): void {\n ChromaMcpManager.uvxAvailabilityProbe = probe;\n }\n\n private static ensureUvOnPath(env: Record<string, string>): void {\n const sep = process.platform === 'win32' ? ';' : ':';\n const pathKey = Object.keys(env).find(k => k.toLowerCase() === 'path') ?? 'PATH';\n const current = env[pathKey] ? env[pathKey].split(sep).filter(Boolean) : [];\n const have = new Set(current.map(p => (process.platform === 'win32' ? p.toLowerCase() : p)));\n const additions = ChromaMcpManager.uvBinDirs().filter(dir => {\n try {\n if (!fs.existsSync(dir)) return false;\n } catch {\n return false;\n }\n const key = process.platform === 'win32' ? dir.toLowerCase() : dir;\n return !have.has(key);\n });\n if (additions.length > 0) {\n env[pathKey] = [...additions, ...current].join(sep);\n logger.debug('CHROMA_MCP', 'Prepended uv bin dir(s) to chroma child PATH', { added: additions });\n }\n }\n\n private static getUvxPreflightEnv(): Record<string, string> {\n const baseEnv: Record<string, string> = {};\n for (const [key, value] of Object.entries(sanitizeEnv(process.env))) {\n if (value !== undefined) {\n baseEnv[key] = value;\n }\n }\n\n // Ensure uvx is resolvable even if the worker's inherited PATH omits uv's\n // bin dir (#2790).\n ChromaMcpManager.ensureUvOnPath(baseEnv);\n\n // Disable Chroma's anonymous telemetry — it issues background HTTP from\n // the embedding subprocess on every collection touch.\n if (!baseEnv.ANONYMIZED_TELEMETRY) baseEnv.ANONYMIZED_TELEMETRY = 'false';\n return baseEnv;\n }\n\n private getSpawnEnv(preflightEnv?: Record<string, string>): Record<string, string> {\n const baseEnv = preflightEnv ? { ...preflightEnv } : ChromaMcpManager.getUvxPreflightEnv();\n\n const combinedCertPath = this.getCombinedCertPath();\n if (!combinedCertPath) {\n return baseEnv;\n }\n\n logger.info('CHROMA_MCP', 'Using combined SSL certificates for enterprise compatibility', {\n certPath: combinedCertPath\n });\n\n return {\n ...baseEnv,\n SSL_CERT_FILE: combinedCertPath,\n REQUESTS_CA_BUNDLE: combinedCertPath,\n CURL_CA_BUNDLE: combinedCertPath,\n NODE_EXTRA_CA_CERTS: combinedCertPath\n };\n }\n\n private registerManagedProcess(): void {\n const chromaProcess = (this.transport as unknown as { _process?: ChildProcess })._process;\n if (!chromaProcess?.pid) {\n return;\n }\n\n // Register with pgid so the supervisor's shutdown cascade can use\n // process-group signaling (kill(-pgid, signal)) to tear down the\n // entire spawn chain (uvx -> uv -> python -> chroma-mcp) in one\n // syscall, matching the SDK subprocess pattern in process-registry.ts.\n //\n // Note: MCP SDK's StdioClientTransport does NOT use detached:true,\n // so the child shares our process group — setting pgid here enables\n // tree-kill via signalProcess() in shutdown.ts which falls back to\n // taskkill /T on Windows when pgid is present but group signal fails.\n // On POSIX the pgid recorded here is used by killProcessTree() in\n // stop() for explicit tree teardown rather than negative-PID signaling.\n getSupervisor().registerProcess(CHROMA_SUPERVISOR_ID, {\n pid: chromaProcess.pid,\n type: 'chroma',\n startedAt: new Date().toISOString(),\n // Store pid as pgid — shutdown.ts will attempt kill(-pgid) on POSIX.\n // If the child isn't actually its own group leader, the ESRCH is caught\n // and shutdown falls back to single-PID kill (see signalProcess()).\n pgid: chromaProcess.pid\n }, chromaProcess);\n\n chromaProcess.once('exit', () => {\n getSupervisor().unregisterProcess(CHROMA_SUPERVISOR_ID);\n });\n }\n}\n","\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { paths } from '../shared/paths.js';\nimport { emitDiagnostic } from '../shared/hook-io.js';\n\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n SILENT = 4\n}\n\nexport type Component =\n | 'AGENTS_MD'\n | 'BRANCH'\n | 'CHROMA'\n | 'CHROMA_MCP'\n | 'CHROMA_SYNC'\n | 'CLAUDE_MD'\n | 'CONFIG'\n | 'CONSOLE'\n | 'CURSOR'\n | 'DB'\n | 'DEDUP'\n | 'ENV'\n | 'FOLDER_INDEX'\n | 'GIT'\n | 'HOOK'\n | 'HTTP'\n | 'IMPORT'\n | 'INGEST'\n | 'OAUTH'\n | 'OPENCLAW'\n | 'OPENCODE'\n | 'PARSER'\n | 'PROCESS'\n | 'PROJECT_NAME'\n | 'QUEUE'\n | 'SDK'\n | 'SDK_SPAWN'\n | 'SEARCH'\n | 'SECURITY'\n | 'SESSION'\n | 'SETTINGS'\n | 'SHUTDOWN'\n | 'SYSTEM'\n | 'TELEGRAM'\n | 'TRANSCRIPT'\n | 'WINDSURF'\n | 'WORKER';\n\ninterface LogContext {\n sessionId?: string | number;\n memorySessionId?: string;\n correlationId?: string | number;\n [key: string]: any;\n}\n\n/**\n * Optional error sink. The logger must NEVER import the telemetry client (that\n * would create an import cycle: telemetry → logger via instrument.ts → ...).\n * Instead worker/telemetry init injects a sink via logger.setErrorSink(); when\n * present, logger.error()/logger.failure() route their Error payload through it\n * (consent + rate-limit + kill-switch all enforced INSIDE the sink, i.e.\n * captureException). The sink is optional and swallow-all so logging keeps\n * working with telemetry disabled or uninstalled.\n */\nexport type ErrorSink = (err: unknown, ctx?: Record<string, unknown>) => void;\nlet errorSink: ErrorSink | null = null;\n\n\nclass Logger {\n private level: LogLevel | null = null;\n private useColor: boolean;\n private logFilePath: string | null = null;\n private logFileInitialized: boolean = false;\n\n constructor() {\n this.useColor = process.stdout.isTTY ?? false;\n // Don't initialize log file in constructor - do it lazily to avoid circular dependency\n }\n\n private ensureLogFileInitialized(): void {\n if (this.logFileInitialized) return;\n this.logFileInitialized = true;\n\n try {\n const logsDir = paths.logsDir();\n\n if (!existsSync(logsDir)) {\n mkdirSync(logsDir, { recursive: true });\n }\n\n const date = new Date().toISOString().split('T')[0];\n this.logFilePath = join(logsDir, `claude-mem-${date}.log`);\n } catch (error: unknown) {\n console.error('[LOGGER] Failed to initialize log file:', error instanceof Error ? error.message : String(error));\n this.logFilePath = null;\n }\n }\n\n private getLevel(): LogLevel {\n if (this.level === null) {\n try {\n const settingsPath = paths.settings();\n if (existsSync(settingsPath)) {\n const settingsData = readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(settingsData);\n const envLevel = (settings.CLAUDE_MEM_LOG_LEVEL || 'INFO').toUpperCase();\n this.level = LogLevel[envLevel as keyof typeof LogLevel] ?? LogLevel.INFO;\n } else {\n this.level = LogLevel.INFO;\n }\n } catch (error: unknown) {\n console.error('[LOGGER] Failed to load log level from settings:', error instanceof Error ? error.message : String(error));\n this.level = LogLevel.INFO;\n }\n }\n return this.level;\n }\n\n private formatData(data: any): string {\n if (data === null || data === undefined) return '';\n if (typeof data === 'string') return data;\n if (typeof data === 'number') return data.toString();\n if (typeof data === 'boolean') return data.toString();\n\n if (typeof data === 'object') {\n if (data instanceof Error) {\n return this.getLevel() === LogLevel.DEBUG\n ? `${data.message}\\n${data.stack}`\n : data.message;\n }\n\n if (Array.isArray(data)) {\n return `[${data.length} items]`;\n }\n\n const keys = Object.keys(data);\n if (keys.length === 0) return '{}';\n if (keys.length <= 3) {\n return JSON.stringify(data);\n }\n return `{${keys.length} keys: ${keys.slice(0, 3).join(', ')}...}`;\n }\n\n return String(data);\n }\n\n formatTool(toolName: string, toolInput?: any): string {\n if (!toolInput) return toolName;\n\n let input = toolInput;\n if (typeof toolInput === 'string') {\n try {\n input = JSON.parse(toolInput);\n } catch (_parseError: unknown) {\n input = toolInput;\n }\n }\n\n if (toolName === 'Bash' && input.command) {\n return `${toolName}(${input.command})`;\n }\n\n if (input.file_path) {\n return `${toolName}(${input.file_path})`;\n }\n\n if (input.notebook_path) {\n return `${toolName}(${input.notebook_path})`;\n }\n\n if (toolName === 'Glob' && input.pattern) {\n return `${toolName}(${input.pattern})`;\n }\n\n if (toolName === 'Grep' && input.pattern) {\n return `${toolName}(${input.pattern})`;\n }\n\n if (input.url) {\n return `${toolName}(${input.url})`;\n }\n\n if (input.query) {\n return `${toolName}(${input.query})`;\n }\n\n if (toolName === 'Task') {\n if (input.subagent_type) {\n return `${toolName}(${input.subagent_type})`;\n }\n if (input.description) {\n return `${toolName}(${input.description})`;\n }\n }\n\n if (toolName === 'Skill' && input.skill) {\n return `${toolName}(${input.skill})`;\n }\n\n if (toolName === 'LSP' && input.operation) {\n return `${toolName}(${input.operation})`;\n }\n\n return toolName;\n }\n\n private formatTimestamp(date: Date): string {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n const hours = String(date.getHours()).padStart(2, '0');\n const minutes = String(date.getMinutes()).padStart(2, '0');\n const seconds = String(date.getSeconds()).padStart(2, '0');\n const ms = String(date.getMilliseconds()).padStart(3, '0');\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;\n }\n\n private log(\n level: LogLevel,\n component: Component,\n message: string,\n context?: LogContext,\n data?: any\n ): void {\n if (level < this.getLevel()) return;\n\n this.ensureLogFileInitialized();\n\n const timestamp = this.formatTimestamp(new Date());\n const levelStr = LogLevel[level].padEnd(5);\n const componentStr = component.padEnd(6);\n\n let correlationStr = '';\n if (context?.correlationId) {\n correlationStr = `[${context.correlationId}] `;\n } else if (context?.sessionId) {\n correlationStr = `[session-${context.sessionId}] `;\n }\n\n let dataStr = '';\n if (data !== undefined && data !== null) {\n if (data instanceof Error) {\n dataStr = this.getLevel() === LogLevel.DEBUG\n ? `\\n${data.message}\\n${data.stack}`\n : ` ${data.message}`;\n } else if (this.getLevel() === LogLevel.DEBUG && typeof data === 'object') {\n try {\n dataStr = '\\n' + JSON.stringify(data, null, 2);\n } catch {\n dataStr = ' ' + this.formatData(data);\n }\n } else {\n dataStr = ' ' + this.formatData(data);\n }\n }\n\n let contextStr = '';\n if (context) {\n const { sessionId, memorySessionId, correlationId, ...rest } = context;\n if (Object.keys(rest).length > 0) {\n const pairs = Object.entries(rest).map(([k, v]) => `${k}=${v}`);\n contextStr = ` {${pairs.join(', ')}}`;\n }\n }\n\n const logLine = `[${timestamp}] [${levelStr}] [${componentStr}] ${correlationStr}${message}${contextStr}${dataStr}`;\n\n if (this.logFilePath) {\n try {\n appendFileSync(this.logFilePath, logLine + '\\n', 'utf8');\n } catch (error: unknown) {\n // DIAGNOSTIC: route through hook-io so the message bypasses the Phase 2\n // hook stderr buffer (#2292). Outside the hook context emitDiagnostic\n // writes straight to real stderr, so non-hook callers are unaffected.\n emitDiagnostic(`[LOGGER] Failed to write to log file: ${error instanceof Error ? error.message : String(error)}\\n`);\n }\n } else {\n // DIAGNOSTIC: see note above.\n emitDiagnostic(logLine + '\\n');\n }\n }\n\n debug(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.DEBUG, component, message, context, data);\n }\n\n info(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.INFO, component, message, context, data);\n }\n\n warn(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.WARN, component, message, context, data);\n }\n\n /**\n * Installs (or clears, with null) the optional error sink. Called once by\n * worker/telemetry init to bridge logged errors into captureException without\n * the logger importing telemetry (no import cycle). Never throws.\n */\n setErrorSink(sink: ErrorSink | null): void {\n errorSink = sink;\n }\n\n error(component: Component, message: string, context?: LogContext, data?: any): void {\n this.log(LogLevel.ERROR, component, message, context, data);\n this.routeErrorToSink(message, context, data);\n }\n\n /**\n * Routes a logged Error through the optional error sink (captureException).\n * Only fires when `data` is an actual Error so we never ship arbitrary log\n * payloads as exceptions. Swallow-all: the sink failing (or being absent)\n * must never break logging. `failure()` delegates to `error()`, so it is\n * covered too — but it passes the same `data` object, so we de-dupe by only\n * routing from the single `error()` entry point.\n */\n private routeErrorToSink(message: string, context?: LogContext, data?: any): void {\n try {\n if (!errorSink || !(data instanceof Error)) return;\n // Pass the message as context so the sink can fingerprint on it too; the\n // sink (captureException) scrubs everything through error-scrub /\n // scrubProperties, so an unsafe message here cannot leak — but `message`\n // is not whitelisted, so it is dropped by scrubProperties anyway. We pass\n // only the Error itself; context is intentionally minimal.\n errorSink(data);\n } catch {\n // Telemetry/error-sink must never break logging.\n }\n }\n\n dataIn(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `→ ${message}`, context, data);\n }\n\n dataOut(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `← ${message}`, context, data);\n }\n\n success(component: Component, message: string, context?: LogContext, data?: any): void {\n this.info(component, `✓ ${message}`, context, data);\n }\n\n failure(component: Component, message: string, context?: LogContext, data?: any): void {\n this.error(component, `✗ ${message}`, context, data);\n }\n\n happyPathError<T = string>(\n component: Component,\n message: string,\n context?: LogContext,\n data?: any,\n fallback: T = '' as T\n ): T {\n const stack = new Error().stack || '';\n const stackLines = stack.split('\\n');\n const callerLine = stackLines[2] || '';\n const callerMatch = callerLine.match(/at\\s+(?:.*\\s+)?\\(?([^:]+):(\\d+):(\\d+)\\)?/);\n const location = callerMatch\n ? `${callerMatch[1].split('/').pop()}:${callerMatch[2]}`\n : 'unknown';\n\n const enhancedContext = {\n ...context,\n location\n };\n\n this.warn(component, `[HAPPY-PATH] ${message}`, enhancedContext, data);\n\n return fallback;\n }\n}\n\nexport const logger = new Logger();\n","import { join, dirname, basename, sep } from 'path';\nimport { homedir } from 'os';\nimport { existsSync, mkdirSync, readFileSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { fileURLToPath } from 'url';\nimport { SettingsDefaultsManager } from './SettingsDefaultsManager.js';\nimport { logger } from '../utils/logger.js';\n\nfunction getDirname(): string {\n if (typeof __dirname !== 'undefined') {\n return __dirname;\n }\n return dirname(fileURLToPath(import.meta.url));\n}\n\nconst _dirname = getDirname();\n\nexport function resolveDataDir(): string {\n if (process.env.CLAUDE_MEM_DATA_DIR) {\n return process.env.CLAUDE_MEM_DATA_DIR;\n }\n\n const defaultDataDir = join(homedir(), '.claude-mem');\n const settingsPath = join(defaultDataDir, 'settings.json');\n try {\n if (existsSync(settingsPath)) {\n const raw = JSON.parse(readFileSync(settingsPath, 'utf-8'));\n const settings = raw.env ?? raw; \n if (settings.CLAUDE_MEM_DATA_DIR) {\n return settings.CLAUDE_MEM_DATA_DIR;\n }\n }\n } catch {\n // settings file missing or corrupt — fall through to default\n }\n\n return defaultDataDir;\n}\n\nexport const DATA_DIR = resolveDataDir();\nexport const CLAUDE_CONFIG_DIR = process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude');\n\nexport const MARKETPLACE_ROOT = join(CLAUDE_CONFIG_DIR, 'plugins', 'marketplaces', 'thedotmack');\n\nexport const ARCHIVES_DIR = join(DATA_DIR, 'archives');\nexport const LOGS_DIR = join(DATA_DIR, 'logs');\nexport const TRASH_DIR = join(DATA_DIR, 'trash');\nexport const BACKUPS_DIR = join(DATA_DIR, 'backups');\nexport const MODES_DIR = join(DATA_DIR, 'modes');\nexport const USER_SETTINGS_PATH = join(DATA_DIR, 'settings.json');\nexport const DB_PATH = join(DATA_DIR, 'claude-mem.db');\nexport const VECTOR_DB_DIR = join(DATA_DIR, 'vector-db');\n\nexport const OBSERVER_SESSIONS_DIR = join(DATA_DIR, 'observer-sessions');\n\nexport const OBSERVER_SESSIONS_PROJECT = basename(OBSERVER_SESSIONS_DIR);\n\nexport const CLAUDE_SETTINGS_PATH = join(CLAUDE_CONFIG_DIR, 'settings.json');\nexport const CLAUDE_COMMANDS_DIR = join(CLAUDE_CONFIG_DIR, 'commands');\nexport const CLAUDE_MD_PATH = join(CLAUDE_CONFIG_DIR, 'CLAUDE.md');\n\nexport function getProjectArchiveDir(projectName: string): string {\n return join(ARCHIVES_DIR, projectName);\n}\n\nexport function ensureDir(dirPath: string): void {\n mkdirSync(dirPath, { recursive: true });\n}\n\nexport function ensureAllDataDirs(): void {\n ensureDir(DATA_DIR);\n ensureDir(ARCHIVES_DIR);\n ensureDir(LOGS_DIR);\n ensureDir(TRASH_DIR);\n ensureDir(BACKUPS_DIR);\n ensureDir(MODES_DIR);\n}\n\nexport function ensureModesDir(): void {\n ensureDir(MODES_DIR);\n}\n\nexport function ensureAllClaudeDirs(): void {\n ensureDir(CLAUDE_CONFIG_DIR);\n ensureDir(CLAUDE_COMMANDS_DIR);\n}\n\nexport function getCurrentProjectName(): string {\n try {\n const gitRoot = execSync('git rev-parse --show-toplevel', {\n cwd: process.cwd(),\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'ignore'],\n windowsHide: true\n }).trim();\n return basename(dirname(gitRoot)) + '/' + basename(gitRoot);\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'Git root detection failed, using cwd basename', {\n cwd: process.cwd()\n }, error instanceof Error ? error : new Error(String(error)));\n const cwd = process.cwd();\n return basename(dirname(cwd)) + '/' + basename(cwd);\n }\n}\n\nexport function getPackageRoot(): string {\n return join(_dirname, '..');\n}\n\nexport function getPackageCommandsDir(): string {\n const packageRoot = getPackageRoot();\n return join(packageRoot, 'commands');\n}\n\nexport const paths = {\n dataDir: () => DATA_DIR,\n workerPid: () => join(DATA_DIR, 'worker.pid'),\n // Phase 1b: identifier renamed to `server*`; the on-disk file basenames\n // remain `.server-beta.*` so existing installations keep finding their\n // pid/port/runtime state. Plan §1d will migrate the basenames.\n serverPid: () => join(DATA_DIR, '.server-beta.pid'),\n serverPort: () => join(DATA_DIR, '.server-beta.port'),\n serverRuntime: () => join(DATA_DIR, '.server-beta.runtime.json'),\n settings: () => join(DATA_DIR, 'settings.json'),\n database: () => join(DATA_DIR, 'claude-mem.db'),\n chroma: () => join(DATA_DIR, 'chroma'),\n combinedCerts: () => join(DATA_DIR, 'combined_certs.pem'),\n transcriptsConfig: () => join(DATA_DIR, 'transcript-watch.json'),\n transcriptsState: () => join(DATA_DIR, 'transcript-watch-state.json'),\n corpora: () => join(DATA_DIR, 'corpora'),\n supervisorRegistry: () => join(DATA_DIR, 'supervisor.json'),\n envFile: () => join(DATA_DIR, '.env'),\n logsDir: () => LOGS_DIR,\n archives: () => ARCHIVES_DIR,\n trash: () => TRASH_DIR,\n backups: () => BACKUPS_DIR,\n modes: () => MODES_DIR,\n vectorDb: () => VECTOR_DB_DIR,\n observerSessions: () => OBSERVER_SESSIONS_DIR,\n} as const;\n","/**\n * Hook IO Discipline (issue #2292)\n *\n * This module is the ONLY place in the hook execution path that calls\n * console.log / process.stderr.write / process.exit. Every emit point declares\n * an intent and routes through here so stdout (MODEL_CONTEXT), stderr\n * (DIAGNOSTIC / USER_HINT), and exit codes (EXIT_SIGNAL / BLOCKING_FEEDBACK)\n * never get conflated.\n *\n * Intent vocabulary:\n * - DIAGNOSTIC operator-visible logs, never reaches the model. stderr.\n * - MODEL_CONTEXT content the assistant consumes. stdout JSON only.\n * - USER_HINT short advisory shown to the human, via HookResult.systemMessage.\n * - BLOCKING_FEEDBACK error message the model must see (stderr + exit 2).\n * - EXIT_SIGNAL pure status, no payload (exit 0).\n *\n * Lives in src/shared/ (not src/cli/) so that src/shared/worker-utils.ts and\n * src/utils/logger.ts can route their stderr through emitDiagnostic without a\n * shared->cli runtime dependency. Only the HookResult / PlatformAdapter TYPES\n * are imported from src/cli, and `import type` is erased at runtime.\n */\nimport type { PlatformAdapter, HookResult } from '../cli/types.js';\n\nexport interface HookStderrBuffer {\n /** Write buffered bytes to real stderr, then clear the buffer. */\n flush(): void;\n /** Discard buffered bytes without writing them. */\n drop(): void;\n /** Un-replace process.stderr.write (idempotent). */\n restore(): void;\n}\n\ntype StderrWriter = (chunk: string | Uint8Array) => boolean;\n\n/**\n * The bypass channel: emitDiagnostic, emitBlockingError, and the buffer's\n * flush() all write through this so they skip the buffered window.\n *\n * - When NO buffer is installed it resolves to the live process.stderr.write\n * (so non-hook callers — worker daemon, CLI — write straight to stderr).\n * - installHookStderrBuffer() pins it to the writer that was active at install\n * time (the real fd writer), so flushing the buffer never re-enters the\n * buffered writer.\n */\nlet pinnedBypassWrite: StderrWriter | null = null;\n\nfunction bypassWrite(chunk: string | Uint8Array): boolean {\n const writer = pinnedBypassWrite\n ?? (process.stderr.write.bind(process.stderr) as StderrWriter);\n return writer(chunk);\n}\n\nlet bufferedChunks: string[] | null = null;\nlet bufferInstalled = false;\n\n/**\n * Replace process.stderr.write with a buffered writer. Direct\n * process.stderr.write calls (including unsolicited third-party library noise)\n * are captured into a buffer; emitDiagnostic / emitBlockingError write through\n * the bypass channel (realStderrWrite). The buffer is flushed when claude-mem\n * chooses to surface, and dropped on graceful success.\n */\nexport function installHookStderrBuffer(): HookStderrBuffer {\n // Pin the currently-active stderr writer as the bypass channel BEFORE we\n // replace process.stderr.write, so flush()/emitDiagnostic write to the real\n // fd and never re-enter the buffered writer.\n const realStderrWrite = process.stderr.write.bind(process.stderr) as StderrWriter;\n pinnedBypassWrite = realStderrWrite;\n bufferedChunks = [];\n bufferInstalled = true;\n\n process.stderr.write = ((chunk: string | Uint8Array): boolean => {\n if (bufferedChunks) {\n bufferedChunks.push(typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf-8'));\n }\n return true;\n }) as typeof process.stderr.write;\n\n return {\n flush(): void {\n if (bufferedChunks && bufferedChunks.length > 0) {\n realStderrWrite(bufferedChunks.join(''));\n }\n bufferedChunks = [];\n },\n drop(): void {\n bufferedChunks = [];\n },\n restore(): void {\n if (!bufferInstalled) return;\n process.stderr.write = realStderrWrite as typeof process.stderr.write;\n bufferInstalled = false;\n bufferedChunks = null;\n pinnedBypassWrite = null;\n },\n };\n}\n\n/**\n * Operator-visible diagnostic. Always reaches real stderr (bypasses the\n * buffer). Use for logger fallback, fail-loud counter, and any \"we want this\n * in the operator's terminal\" message. Takes a raw string; keep logger.* as\n * the structured-logging path.\n */\nexport function emitDiagnostic(line: string): void {\n bypassWrite(line);\n}\n\n/**\n * Emit the model-bound JSON payload to stdout. Calls adapter.formatOutput and\n * JSON.stringify exactly once. Throws if called twice in the same emitter\n * lifetime (guards against double-emit corrupting the stdout JSON stream).\n *\n * Uses console.log (not process.stdout.write) on purpose: the trailing newline\n * is what Claude Code's / Codex's hook parser expects.\n */\nexport function emitModelContext(adapter: PlatformAdapter, result: HookResult): void {\n if (moduleHasEmitted) {\n throw new Error('emitModelContext called twice');\n }\n moduleHasEmitted = true;\n const output = adapter.formatOutput(result);\n console.log(JSON.stringify(output));\n}\n\nlet moduleHasEmitted = false;\n\n/**\n * USER_HINT routed via the HookResult.systemMessage path. Does NOT write to a\n * stream — returns a HookResult the caller merges before emitModelContext.\n * systemMessage is platform-specific (claude-code surfaces it; codex ignores\n * it) so it must flow through the adapter, not raw stderr. If a systemMessage\n * already exists, the hint is appended with a blank line between.\n */\nexport function withUserHint(result: HookResult, hint: string): HookResult {\n const merged = result.systemMessage ? `${result.systemMessage}\\n\\n${hint}` : hint;\n return { ...result, systemMessage: merged };\n}\n\nexport interface ExitOptions {\n skipExit?: boolean;\n}\n\n/**\n * BLOCKING_FEEDBACK: flush buffered stderr (so preceding diagnostics reach the\n * operator/model), write `msg` to real stderr, then exit 2 so the model\n * receives it per Claude Code's hook contract. `skipExit` is the test seam\n * that mirrors HookCommandOptions.skipExit.\n */\nexport function emitBlockingError(msg: string, options: ExitOptions = {}): void {\n if (bufferedChunks && bufferedChunks.length > 0) {\n bypassWrite(bufferedChunks.join(''));\n bufferedChunks = [];\n }\n bypassWrite(msg.endsWith('\\n') ? msg : `${msg}\\n`);\n if (!options.skipExit) {\n process.exit(2);\n }\n}\n\n/**\n * EXIT_SIGNAL: drop any buffered stderr (preserving the quiet-on-success /\n * Windows Terminal tab-management behavior) and exit 0. Caller is expected to\n * have already emitted any required stdout JSON envelope.\n */\nexport function exitGraceful(options: ExitOptions = {}): void {\n if (bufferedChunks) {\n bufferedChunks = [];\n }\n if (!options.skipExit) {\n process.exit(0);\n }\n}\n\n/**\n * Reset the per-invocation emit flag. hookCommand calls this at the start of\n * each invocation so the emitModelContext double-emit guard is per-hook, not\n * per-process (matters for the in-process test harness and skipExit tests).\n */\nexport function resetHookIoState(): void {\n moduleHasEmitted = false;\n}\n","\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { homedir } from 'os';\nimport { HOOK_TIMEOUTS, getTimeout } from './hook-constants.js';\n\nexport interface SettingsDefaults {\n CLAUDE_MEM_MODEL: string;\n CLAUDE_MEM_CONTEXT_OBSERVATIONS: string;\n CLAUDE_MEM_WORKER_PORT: string;\n CLAUDE_MEM_WORKER_HOST: string;\n CLAUDE_MEM_API_TIMEOUT_MS: string;\n CLAUDE_MEM_SKIP_TOOLS: string;\n CLAUDE_MEM_PROVIDER: string; \n CLAUDE_MEM_CLAUDE_AUTH_METHOD: string; \n CLAUDE_MEM_GEMINI_API_KEY: string;\n CLAUDE_MEM_GEMINI_MODEL: string; \n CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED: string;\n CLAUDE_MEM_OPENROUTER_API_KEY: string;\n CLAUDE_MEM_OPENROUTER_MODEL: string;\n CLAUDE_MEM_OPENROUTER_BASE_URL: string;\n CLAUDE_MEM_OPENROUTER_SITE_URL: string;\n CLAUDE_MEM_OPENROUTER_APP_NAME: string;\n CLAUDE_MEM_DATA_DIR: string;\n CLAUDE_MEM_LOG_LEVEL: string;\n CLAUDE_MEM_PYTHON_VERSION: string;\n CLAUDE_CODE_PATH: string;\n CLAUDE_MEM_MODE: string;\n CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: string;\n CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: string;\n CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: string;\n CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: string;\n CLAUDE_MEM_CONTEXT_FULL_COUNT: string;\n CLAUDE_MEM_CONTEXT_FULL_FIELD: string;\n CLAUDE_MEM_CONTEXT_SESSION_COUNT: string;\n CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: string;\n CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: string;\n CLAUDE_MEM_CONTEXT_SHOW_TERMINAL_OUTPUT: string;\n CLAUDE_MEM_WELCOME_HINT_ENABLED: string;\n CLAUDE_MEM_FOLDER_CLAUDEMD_ENABLED: string;\n CLAUDE_MEM_FOLDER_USE_LOCAL_MD: string; \n CLAUDE_MEM_TRANSCRIPTS_ENABLED: string; \n CLAUDE_MEM_TRANSCRIPTS_CONFIG_PATH: string; \n CLAUDE_MEM_CODEX_TRANSCRIPT_INGESTION: string;\n CLAUDE_MEM_MAX_CONCURRENT_AGENTS: string; \n CLAUDE_MEM_HOOK_FAIL_LOUD_THRESHOLD: string; \n CLAUDE_MEM_EXCLUDED_PROJECTS: string; \n CLAUDE_MEM_FOLDER_MD_EXCLUDE: string;\n CLAUDE_MEM_FOLDER_MD_SKELETON_DENYLIST: string;\n CLAUDE_MEM_SEMANTIC_INJECT: string; \n CLAUDE_MEM_SEMANTIC_INJECT_LIMIT: string; \n CLAUDE_MEM_TIER_ROUTING_ENABLED: string;\n CLAUDE_MEM_TIER_SIMPLE_MODEL: string;\n CLAUDE_MEM_TIER_SUMMARY_MODEL: string;\n CLAUDE_MEM_TIER_FAST_MODEL: string; // #2289 — resolved by $TIER:fast in CLAUDE_MEM_MODEL\n CLAUDE_MEM_TIER_SMART_MODEL: string; // #2289 — resolved by $TIER:smart in CLAUDE_MEM_MODEL\n CLAUDE_MEM_CHROMA_ENABLED: string; \n CLAUDE_MEM_CHROMA_MODE: string; \n CLAUDE_MEM_CHROMA_HOST: string;\n CLAUDE_MEM_CHROMA_PORT: string;\n CLAUDE_MEM_CHROMA_SSL: string;\n CLAUDE_MEM_CHROMA_API_KEY: string;\n CLAUDE_MEM_CHROMA_TENANT: string;\n CLAUDE_MEM_CHROMA_DATABASE: string;\n CLAUDE_MEM_CHROMA_PREWARM_TIMEOUT_MS: string;\n CLAUDE_MEM_TELEGRAM_ENABLED: string;\n CLAUDE_MEM_TELEGRAM_BOT_TOKEN: string;\n CLAUDE_MEM_TELEGRAM_CHAT_ID: string;\n CLAUDE_MEM_TELEGRAM_TRIGGER_TYPES: string;\n CLAUDE_MEM_TELEGRAM_TRIGGER_CONCEPTS: string;\n CLAUDE_MEM_QUEUE_ENGINE: string;\n CLAUDE_MEM_REDIS_URL: string;\n CLAUDE_MEM_REDIS_HOST: string;\n CLAUDE_MEM_REDIS_PORT: string;\n CLAUDE_MEM_REDIS_MODE: string;\n CLAUDE_MEM_QUEUE_REDIS_PREFIX: string;\n CLAUDE_MEM_AUTH_MODE: string;\n CLAUDE_MEM_RUNTIME: string;\n // Phase 1a (cmem-sdk rename): canonical server settings keys. Hooks read\n // these first and fall back to the legacy `*_BETA_*` keys below.\n CLAUDE_MEM_SERVER_URL: string;\n CLAUDE_MEM_SERVER_API_KEY: string;\n CLAUDE_MEM_SERVER_PROJECT_ID: string;\n // Legacy keys retained for back-compat with existing settings.json files.\n CLAUDE_MEM_SERVER_BETA_URL: string;\n CLAUDE_MEM_SERVER_BETA_API_KEY: string;\n CLAUDE_MEM_SERVER_BETA_PROJECT_ID: string;\n}\n\nexport class SettingsDefaultsManager {\n private static readonly DEFAULTS: SettingsDefaults = {\n CLAUDE_MEM_MODEL: 'claude-haiku-4-5-20251001',\n CLAUDE_MEM_CONTEXT_OBSERVATIONS: '50',\n CLAUDE_MEM_WORKER_PORT: String(37700 + ((process.getuid?.() ?? 77) % 100)),\n CLAUDE_MEM_WORKER_HOST: '127.0.0.1',\n CLAUDE_MEM_API_TIMEOUT_MS: String(getTimeout(HOOK_TIMEOUTS.API_REQUEST)),\n CLAUDE_MEM_SKIP_TOOLS: 'ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion',\n CLAUDE_MEM_PROVIDER: 'claude', // Default to Claude\n CLAUDE_MEM_CLAUDE_AUTH_METHOD: 'subscription', // Default to logged-in Claude SDK auth (not API key)\n CLAUDE_MEM_GEMINI_API_KEY: '', // Empty by default, can be set via UI or env\n CLAUDE_MEM_GEMINI_MODEL: 'gemini-2.5-flash-lite', // Default Gemini model (highest free tier RPM)\n CLAUDE_MEM_GEMINI_RATE_LIMITING_ENABLED: 'true', // Rate limiting ON by default for free tier users\n CLAUDE_MEM_OPENROUTER_API_KEY: '', // Empty by default, can be set via UI or env\n CLAUDE_MEM_OPENROUTER_MODEL: 'xiaomi/mimo-v2-flash:free', // Default OpenRouter model (free tier)\n CLAUDE_MEM_OPENROUTER_BASE_URL: '', // #2382/#2590/#2622/#2393 — optional OpenAI-compatible base URL (e.g. https://api.deepseek.com, http://localhost:1234/v1). Empty = default OpenRouter endpoint.\n CLAUDE_MEM_OPENROUTER_SITE_URL: '', // Optional: for OpenRouter analytics\n CLAUDE_MEM_OPENROUTER_APP_NAME: 'claude-mem', // App name for OpenRouter analytics\n CLAUDE_MEM_DATA_DIR: join(homedir(), '.claude-mem'),\n CLAUDE_MEM_LOG_LEVEL: 'INFO',\n CLAUDE_MEM_PYTHON_VERSION: '3.13',\n CLAUDE_CODE_PATH: '', // Empty means auto-detect via 'which claude'\n CLAUDE_MEM_MODE: 'code', // Default mode profile\n CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS: 'false',\n CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS: 'false',\n CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT: 'false',\n CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT: 'true',\n CLAUDE_MEM_CONTEXT_FULL_COUNT: '0',\n CLAUDE_MEM_CONTEXT_FULL_FIELD: 'narrative',\n CLAUDE_MEM_CONTEXT_SESSION_COUNT: '10',\n CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY: 'true',\n CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE: 'false',\n CLAUDE_MEM_CONTEXT_SHOW_TERMINAL_OUTPUT: 'true',\n CLAUDE_MEM_WELCOME_HINT_ENABLED: 'true',\n CLAUDE_MEM_FOLDER_CLAUDEMD_ENABLED: 'false',\n CLAUDE_MEM_FOLDER_USE_LOCAL_MD: 'false', // When true, writes to CLAUDE.local.md instead of CLAUDE.md\n CLAUDE_MEM_TRANSCRIPTS_ENABLED: 'true',\n CLAUDE_MEM_TRANSCRIPTS_CONFIG_PATH: join(homedir(), '.claude-mem', 'transcript-watch.json'),\n CLAUDE_MEM_CODEX_TRANSCRIPT_INGESTION: 'false',\n CLAUDE_MEM_MAX_CONCURRENT_AGENTS: '2', // Max concurrent Claude SDK agent subprocesses\n CLAUDE_MEM_HOOK_FAIL_LOUD_THRESHOLD: '3', // Plan 05 Phase 8 — escalate to exit code 2 after N consecutive worker-unreachable hook invocations\n CLAUDE_MEM_EXCLUDED_PROJECTS: '', // Comma-separated glob patterns for excluded project paths\n CLAUDE_MEM_FOLDER_MD_EXCLUDE: '[]', // JSON array of folder paths to exclude from CLAUDE.md generation\n CLAUDE_MEM_FOLDER_MD_SKELETON_DENYLIST: '[]', // #2400 — JSON array of glob patterns; when a folder matches AND its generated CLAUDE.md would be empty/skeleton, skip injection (avoids polluting non-content dirs with empty skeletons). Default [] preserves existing behavior.\n CLAUDE_MEM_SEMANTIC_INJECT: 'false', // Inject relevant past observations on every UserPromptSubmit (experimental, disabled by default)\n CLAUDE_MEM_SEMANTIC_INJECT_LIMIT: '5', // Top-N most relevant observations to inject per prompt\n CLAUDE_MEM_TIER_ROUTING_ENABLED: 'true', // Route observations to models by complexity\n CLAUDE_MEM_TIER_SIMPLE_MODEL: 'haiku', // Portable tier alias — works across Direct API, Bedrock, Vertex, Azure (see #1463)\n CLAUDE_MEM_TIER_SUMMARY_MODEL: '', // Empty = use default model for summaries\n CLAUDE_MEM_TIER_FAST_MODEL: 'haiku', // #2289 — $TIER:fast resolves here (portable alias)\n CLAUDE_MEM_TIER_SMART_MODEL: 'sonnet', // #2289 — $TIER:smart resolves here (portable alias)\n CLAUDE_MEM_CHROMA_ENABLED: 'true', // Set to 'false' to disable Chroma and use SQLite-only search\n CLAUDE_MEM_CHROMA_MODE: 'local', // 'local' uses persistent chroma-mcp via uvx, 'remote' connects to existing server\n CLAUDE_MEM_CHROMA_HOST: '127.0.0.1',\n CLAUDE_MEM_CHROMA_PORT: '8000',\n CLAUDE_MEM_CHROMA_SSL: 'false',\n CLAUDE_MEM_CHROMA_API_KEY: '',\n CLAUDE_MEM_CHROMA_TENANT: 'default_tenant',\n CLAUDE_MEM_CHROMA_DATABASE: 'default_database',\n CLAUDE_MEM_CHROMA_PREWARM_TIMEOUT_MS: '120000',\n CLAUDE_MEM_TELEGRAM_ENABLED: 'true',\n CLAUDE_MEM_TELEGRAM_BOT_TOKEN: '',\n CLAUDE_MEM_TELEGRAM_CHAT_ID: '',\n CLAUDE_MEM_TELEGRAM_TRIGGER_TYPES: 'security_alert',\n CLAUDE_MEM_TELEGRAM_TRIGGER_CONCEPTS: '',\n CLAUDE_MEM_QUEUE_ENGINE: 'sqlite',\n CLAUDE_MEM_REDIS_URL: '',\n CLAUDE_MEM_REDIS_HOST: '127.0.0.1',\n CLAUDE_MEM_REDIS_PORT: '6379',\n CLAUDE_MEM_REDIS_MODE: 'external',\n CLAUDE_MEM_QUEUE_REDIS_PREFIX: `claude_mem_${process.env.CLAUDE_MEM_WORKER_PORT ?? String(37700 + ((process.getuid?.() ?? 77) % 100))}`,\n CLAUDE_MEM_AUTH_MODE: 'api-key',\n CLAUDE_MEM_RUNTIME: 'worker',\n // Phase 1a (cmem-sdk rename): canonical server settings keys. Hooks read\n // these first; the legacy `*_BETA_*` defaults below remain so existing\n // settings.json files still resolve correctly.\n CLAUDE_MEM_SERVER_URL: `http://127.0.0.1:${process.env.CLAUDE_MEM_SERVER_PORT ?? String(37877 + ((process.getuid?.() ?? 77) % 100))}`, // Default server runtime URL — UID-derived for multi-account isolation\n CLAUDE_MEM_SERVER_API_KEY: '', // Local hook API key, populated by installer when runtime=server\n CLAUDE_MEM_SERVER_PROJECT_ID: '', // Default Postgres project_id used by hooks when runtime=server\n CLAUDE_MEM_SERVER_BETA_URL: `http://127.0.0.1:${process.env.CLAUDE_MEM_SERVER_PORT ?? String(37877 + ((process.getuid?.() ?? 77) % 100))}`, // Legacy server-beta runtime URL — UID-derived for multi-account isolation\n CLAUDE_MEM_SERVER_BETA_API_KEY: '', // Legacy local hook API key (read as fallback when CLAUDE_MEM_SERVER_API_KEY unset)\n CLAUDE_MEM_SERVER_BETA_PROJECT_ID: '', // Legacy Postgres project_id (read as fallback when CLAUDE_MEM_SERVER_PROJECT_ID unset)\n };\n\n static getAllDefaults(): SettingsDefaults {\n return { ...this.DEFAULTS };\n }\n\n static get(key: keyof SettingsDefaults): string {\n return process.env[key] ?? this.DEFAULTS[key];\n }\n\n static getInt(key: keyof SettingsDefaults): number {\n const value = this.get(key);\n return parseInt(value, 10);\n }\n\n static getBool(key: keyof SettingsDefaults): boolean {\n const value: unknown = this.get(key);\n return value === 'true' || value === true;\n }\n\n private static applyEnvOverrides(settings: SettingsDefaults): SettingsDefaults {\n const result = { ...settings };\n for (const key of Object.keys(this.DEFAULTS) as Array<keyof SettingsDefaults>) {\n if (process.env[key] !== undefined) {\n result[key] = process.env[key]!;\n }\n }\n return result;\n }\n\n static loadFromFile(settingsPath: string, applyEnvOverrides = true): SettingsDefaults {\n try {\n if (!existsSync(settingsPath)) {\n const defaults = this.getAllDefaults();\n try {\n const dir = dirname(settingsPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(settingsPath, JSON.stringify(defaults, null, 2), 'utf-8');\n // stderr, never stdout: this fires on the first boot in a fresh data\n // dir, and CLI commands like `start` promise machine-readable JSON\n // on stdout to the hook framework.\n console.warn('[SETTINGS] Created settings file with defaults:', settingsPath);\n } catch (error: unknown) {\n console.warn('[SETTINGS] Failed to create settings file, using in-memory defaults:', settingsPath, error instanceof Error ? error.message : String(error));\n }\n return applyEnvOverrides ? this.applyEnvOverrides(defaults) : defaults;\n }\n\n const settingsData = readFileSync(settingsPath, 'utf-8');\n // Strip UTF-8 BOM if present — Windows tools (editors, formatters, CLI\n // hooks) may prepend U+FEFF which Bun's JSON.parse rejects silently,\n // causing a full fallback to defaults and breaking server-beta routing.\n const settings = JSON.parse(settingsData.replace(/^\\uFEFF/, ''));\n\n let flatSettings = settings;\n if (settings.env && typeof settings.env === 'object') {\n flatSettings = settings.env;\n\n try {\n writeFileSync(settingsPath, JSON.stringify(flatSettings, null, 2), 'utf-8');\n // stderr, never stdout — same JSON-on-stdout contract as above.\n console.warn('[SETTINGS] Migrated settings file from nested to flat schema:', settingsPath);\n } catch (error: unknown) {\n console.warn('[SETTINGS] Failed to auto-migrate settings file:', settingsPath, error instanceof Error ? error.message : String(error));\n // Continue with in-memory migration even if write fails\n }\n }\n\n const result: SettingsDefaults = { ...this.DEFAULTS };\n for (const key of Object.keys(this.DEFAULTS) as Array<keyof SettingsDefaults>) {\n if (flatSettings[key] !== undefined) {\n result[key] = flatSettings[key];\n }\n }\n\n return applyEnvOverrides ? this.applyEnvOverrides(result) : result;\n } catch (error: unknown) {\n console.warn('[SETTINGS] Failed to load settings, using defaults:', settingsPath, error instanceof Error ? error.message : String(error));\n const defaults = this.getAllDefaults();\n return applyEnvOverrides ? this.applyEnvOverrides(defaults) : defaults;\n }\n }\n}\n","export const HOOK_TIMEOUTS = {\n DEFAULT: 300000, // Standard HTTP timeout (5 min for slow systems)\n HEALTH_CHECK: 3000, // Worker health check (3s — healthy worker responds in <100ms)\n API_REQUEST: 30000, // Hook API calls should outlive health probes but stay below hook caps\n HOOK_READINESS_WAIT: 10000, // Per-hook wait for an already-starting worker to finish DB/search init\n POST_SPAWN_WAIT: 15000, // Wait for daemon to start after spawn (starts in <1s on Linux, 6-8s on macOS with Chroma)\n READINESS_WAIT: 30000, // Wait for DB + search init after spawn (typically <5s)\n PORT_IN_USE_WAIT: 3000, // Wait when port occupied but health failing\n WORKER_STARTUP_WAIT: 1000,\n PRE_RESTART_SETTLE_DELAY: 2000, // Give files time to sync before restart\n POWERSHELL_COMMAND: 10000, // PowerShell process enumeration (10s - typically completes in <1s)\n WINDOWS_MULTIPLIER: 1.5 \n} as const;\n\nexport const HOOK_EXIT_CODES = {\n SUCCESS: 0,\n FAILURE: 1,\n BLOCKING_ERROR: 2,\n USER_MESSAGE_ONLY: 3,\n} as const;\n\nexport function getTimeout(baseTimeout: number): number {\n return process.platform === 'win32'\n ? Math.round(baseTimeout * HOOK_TIMEOUTS.WINDOWS_MULTIPLIER)\n : baseTimeout;\n}\n","// Filters CLAUDE_CODE_* (and CLAUDECODE_*) unless explicitly preserved in\n// ENV_PRESERVE. This is layer 2 of defense for #2357 (CLAUDE_CODE_EFFORT_LEVEL\n// / CLAUDE_CODE_ALWAYS_ENABLE_EFFORT leaking into the SDK subprocess) — layer 1\n// is BLOCKED_ENV_VARS in EnvManager.ts. Do NOT add the EFFORT_* vars to\n// ENV_PRESERVE: preserving them would defeat the strip.\nexport const ENV_PREFIXES = ['CLAUDECODE_', 'CLAUDE_CODE_'];\nexport const ENV_EXACT_MATCHES = new Set([\n 'CLAUDECODE',\n 'CLAUDE_CODE_SESSION',\n 'CLAUDE_CODE_ENTRYPOINT',\n 'MCP_SESSION_ID',\n // Proxy vars: strip so the SDK subprocess doesn't inherit a proxy config.\n 'HTTP_PROXY',\n 'HTTPS_PROXY',\n 'ALL_PROXY',\n 'NO_PROXY',\n 'http_proxy',\n 'https_proxy',\n 'all_proxy',\n 'no_proxy',\n 'npm_config_proxy',\n 'npm_config_https_proxy',\n]);\n\nexport const ENV_PRESERVE = new Set([\n 'CLAUDE_CODE_OAUTH_TOKEN',\n 'CLAUDE_CODE_GIT_BASH_PATH',\n 'CLAUDE_CODE_USE_BEDROCK',\n 'CLAUDE_CODE_USE_VERTEX',\n 'ANTHROPIC_BEDROCK_BASE_URL',\n 'AWS_REGION',\n 'AWS_PROFILE',\n 'AWS_ACCESS_KEY_ID',\n 'AWS_SECRET_ACCESS_KEY',\n 'AWS_SESSION_TOKEN',\n 'ANTHROPIC_VERTEX_PROJECT_ID',\n 'CLOUD_ML_REGION',\n 'GOOGLE_APPLICATION_CREDENTIALS',\n]);\n\nexport function sanitizeEnv(env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {\n const sanitized: NodeJS.ProcessEnv = {};\n\n for (const [key, value] of Object.entries(env)) {\n if (value === undefined) continue;\n if (ENV_PRESERVE.has(key)) { sanitized[key] = value; continue; }\n if (ENV_EXACT_MATCHES.has(key)) continue;\n if (ENV_PREFIXES.some(prefix => key.startsWith(prefix))) continue;\n sanitized[key] = value;\n }\n\n return sanitized;\n}\n","import { existsSync, readFileSync, rmSync } from 'fs';\nimport { logger } from '../utils/logger.js';\nimport {\n getProcessRegistry,\n verifyPidFileOwnership,\n type ManagedProcessInfo,\n type PidInfo,\n type ProcessRegistry\n} from './process-registry.js';\nimport { runShutdownCascade } from './shutdown.js';\nimport { startHealthChecker, stopHealthChecker } from './health-checker.js';\nimport { paths } from '../shared/paths.js';\n\nconst PID_FILE = paths.workerPid();\n\ninterface ValidateWorkerPidOptions {\n logAlive?: boolean;\n pidFilePath?: string;\n}\n\nexport type ValidateWorkerPidStatus = 'missing' | 'alive' | 'stale' | 'invalid';\n\nclass Supervisor {\n private readonly registry: ProcessRegistry;\n private started = false;\n private stopPromise: Promise<void> | null = null;\n private signalHandlersRegistered = false;\n private shutdownInitiated = false;\n private shutdownHandler: (() => Promise<void>) | null = null;\n\n constructor(registry: ProcessRegistry) {\n this.registry = registry;\n }\n\n async start(): Promise<void> {\n if (this.started) return;\n\n this.registry.initialize();\n const pidStatus = validateWorkerPidFile({ logAlive: false });\n if (pidStatus === 'alive') {\n throw new Error('Worker already running');\n }\n\n this.started = true;\n\n startHealthChecker();\n }\n\n configureSignalHandlers(shutdownHandler: () => Promise<void>): void {\n this.shutdownHandler = shutdownHandler;\n\n if (this.signalHandlersRegistered) return;\n this.signalHandlersRegistered = true;\n\n const handleSignal = async (signal: string): Promise<void> => {\n if (this.shutdownInitiated) {\n logger.warn('SYSTEM', `Received ${signal} but shutdown already in progress`);\n return;\n }\n this.shutdownInitiated = true;\n\n logger.info('SYSTEM', `Received ${signal}, shutting down...`);\n\n try {\n if (this.shutdownHandler) {\n await this.shutdownHandler();\n } else {\n await this.stop();\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.error('SYSTEM', 'Error during shutdown', {}, error);\n } else {\n logger.error('SYSTEM', 'Error during shutdown (non-Error)', { error: String(error) });\n }\n try {\n await this.stop();\n } catch (stopError: unknown) {\n if (stopError instanceof Error) {\n logger.debug('SYSTEM', 'Supervisor shutdown fallback failed', {}, stopError);\n } else {\n logger.debug('SYSTEM', 'Supervisor shutdown fallback failed', { error: String(stopError) });\n }\n }\n }\n\n process.exit(0);\n };\n\n process.on('SIGTERM', () => void handleSignal('SIGTERM'));\n process.on('SIGINT', () => void handleSignal('SIGINT'));\n\n if (process.platform !== 'win32') {\n if (process.argv.includes('--daemon')) {\n process.on('SIGHUP', () => {\n logger.debug('SYSTEM', 'Ignoring SIGHUP in daemon mode');\n });\n } else {\n process.on('SIGHUP', () => void handleSignal('SIGHUP'));\n }\n }\n }\n\n async stop(): Promise<void> {\n if (this.stopPromise) {\n await this.stopPromise;\n return;\n }\n\n stopHealthChecker();\n this.stopPromise = runShutdownCascade({\n registry: this.registry,\n currentPid: process.pid\n }).finally(() => {\n this.started = false;\n this.stopPromise = null;\n });\n\n await this.stopPromise;\n }\n\n assertCanSpawn(type: string): void {\n if (this.stopPromise !== null) {\n throw new Error(`Supervisor is shutting down, refusing to spawn ${type}`);\n }\n }\n\n registerProcess(id: string, processInfo: ManagedProcessInfo, processRef?: Parameters<ProcessRegistry['register']>[2]): void {\n this.registry.register(id, processInfo, processRef);\n }\n\n unregisterProcess(id: string): void {\n this.registry.unregister(id);\n }\n\n getRegistry(): ProcessRegistry {\n return this.registry;\n }\n}\n\nconst supervisorSingleton = new Supervisor(getProcessRegistry());\n\nexport async function startSupervisor(): Promise<void> {\n await supervisorSingleton.start();\n}\n\nexport function getSupervisor(): Supervisor {\n return supervisorSingleton;\n}\n\nexport function configureSupervisorSignalHandlers(shutdownHandler: () => Promise<void>): void {\n supervisorSingleton.configureSignalHandlers(shutdownHandler);\n}\n\nexport function validateWorkerPidFile(options: ValidateWorkerPidOptions = {}): ValidateWorkerPidStatus {\n const pidFilePath = options.pidFilePath ?? PID_FILE;\n\n if (!existsSync(pidFilePath)) {\n return 'missing';\n }\n\n let pidInfo: PidInfo | null = null;\n\n try {\n pidInfo = JSON.parse(readFileSync(pidFilePath, 'utf-8')) as PidInfo | null;\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.warn('SYSTEM', 'Failed to parse worker PID file, removing it', { path: pidFilePath }, error);\n } else {\n logger.warn('SYSTEM', 'Failed to parse worker PID file, removing it', {\n path: pidFilePath,\n error: String(error)\n });\n }\n rmSync(pidFilePath, { force: true });\n return 'invalid';\n }\n\n const isAlive = verifyPidFileOwnership(pidInfo);\n if (isAlive && pidInfo) {\n if (options.logAlive ?? true) {\n logger.info('SYSTEM', 'Worker already running (PID alive)', {\n existingPid: pidInfo.pid,\n existingPort: pidInfo.port,\n startedAt: pidInfo.startedAt\n });\n }\n return 'alive';\n }\n\n logger.info('SYSTEM', 'Removing stale PID file (worker process is dead or PID has been reused)', {\n pid: pidInfo?.pid,\n port: pidInfo?.port,\n startedAt: pidInfo?.startedAt\n });\n rmSync(pidFilePath, { force: true });\n return 'stale';\n}\n","import { ChildProcess, spawnSync } from 'child_process';\nimport { spawnHidden } from '../shared/spawn.js';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport path from 'path';\nimport { logger } from '../utils/logger.js';\nimport { sanitizeEnv } from './env-sanitizer.js';\nimport { paths } from '../shared/paths.js';\n\nconst REAP_SESSION_SIGTERM_TIMEOUT_MS = 5_000;\nconst REAP_SESSION_SIGKILL_TIMEOUT_MS = 1_000;\n\nconst DEFAULT_REGISTRY_PATH = paths.supervisorRegistry();\n\nexport interface ManagedProcessInfo {\n pid: number;\n type: string;\n sessionId?: string | number;\n startedAt: string;\n pgid?: number;\n}\n\nexport interface ManagedProcessRecord extends ManagedProcessInfo {\n id: string;\n}\n\ninterface PersistedRegistry {\n processes: Record<string, ManagedProcessInfo>;\n}\n\nexport function isPidAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid < 0) return false;\n if (pid === 0) return false;\n\n try {\n process.kill(pid, 0);\n return true;\n } catch (error: unknown) {\n if (error instanceof Error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === 'EPERM') return true;\n logger.debug('SYSTEM', 'PID check failed', { pid, code });\n return false;\n }\n logger.warn('SYSTEM', 'PID check threw non-Error', { pid, error: String(error) });\n return false;\n }\n}\n\n// Poll until every record's pid is gone or the timeout elapses. Shared by the\n// reapSession wait phase and shutdown.ts's SIGTERM grace period.\nexport async function waitForExit(records: ManagedProcessRecord[], timeoutMs: number): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (records.every(record => !isPidAlive(record.pid))) {\n return;\n }\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n}\n\nexport interface PidInfo {\n pid: number;\n port: number;\n startedAt: string;\n startToken?: string;\n}\n\n// Windows lacks a cheap /proc-style start-time read and `ps lstart`, so we\n// shell to PowerShell's CIM (wmic is removed on Windows 11). The lookup is\n// ~100-300ms, so cache per-pid for 5s to avoid re-shelling when the same PID\n// is validated repeatedly within one spawn-decision window.\nconst WINDOWS_START_TOKEN_CACHE_TTL_MS = 5_000;\nconst windowsStartTokenCache = new Map<number, { token: string | null; capturedAtMs: number }>();\n\nfunction captureWindowsStartToken(pid: number): string | null {\n const cached = windowsStartTokenCache.get(pid);\n if (cached && Date.now() - cached.capturedAtMs < WINDOWS_START_TOKEN_CACHE_TTL_MS) {\n return cached.token;\n }\n\n let token: string | null = null;\n try {\n // CreationDate is a CIM DATETIME (yyyyMMddHHmmss.ffffff±UTCoffset) that is\n // unique-enough per (pid, boot) to detect PID reuse. `-NoProfile` keeps it\n // fast; sanitizeEnv keeps the spawn-env discipline uniform (#2357/#2375).\n const result = spawnSync(\n 'powershell.exe',\n [\n '-NoProfile',\n '-NonInteractive',\n '-Command',\n `(Get-CimInstance Win32_Process -Filter \\\"ProcessId=${pid}\\\").CreationDate.ToString('yyyyMMddHHmmss.ffffff')`\n ],\n {\n encoding: 'utf-8',\n timeout: 5000,\n windowsHide: true,\n env: { ...sanitizeEnv(process.env), LC_ALL: 'C', LANG: 'C' }\n }\n );\n if (result.status === 0) {\n const trimmed = result.stdout.trim();\n token = trimmed.length > 0 ? trimmed : null;\n }\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'captureProcessStartToken: powershell CIM lookup failed', {\n pid,\n error: error instanceof Error ? error.message : String(error)\n });\n token = null;\n }\n\n windowsStartTokenCache.set(pid, { token, capturedAtMs: Date.now() });\n return token;\n}\n\nexport function captureProcessStartToken(pid: number): string | null {\n if (!Number.isInteger(pid) || pid <= 0) return null;\n\n if (process.platform === 'linux') {\n try {\n const raw = readFileSync(`/proc/${pid}/stat`, 'utf-8');\n const tailStart = raw.lastIndexOf(') ');\n if (tailStart < 0) return null;\n const fields = raw.slice(tailStart + 2).split(' ');\n const starttime = fields[19];\n return starttime && /^\\d+$/.test(starttime) ? starttime : null;\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'captureProcessStartToken: /proc read failed', {\n pid,\n error: error instanceof Error ? error.message : String(error)\n });\n return null;\n }\n }\n\n if (process.platform === 'win32') {\n return captureWindowsStartToken(pid);\n }\n\n try {\n const result = spawnSync('ps', ['-p', String(pid), '-o', 'lstart='], {\n encoding: 'utf-8',\n timeout: 2000,\n // Uniform spawn-env discipline: sanitize even for read-only system\n // binaries so the spawn-env CI check stays a single rule (#2357/#2375).\n env: { ...sanitizeEnv(process.env), LC_ALL: 'C', LANG: 'C' }\n });\n if (result.status !== 0) return null;\n const token = result.stdout.trim();\n return token.length > 0 ? token : null;\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'captureProcessStartToken: ps exec failed', {\n pid,\n error: error instanceof Error ? error.message : String(error)\n });\n return null;\n }\n}\n\nexport function verifyPidFileOwnership(info: PidInfo | null): info is PidInfo {\n if (!info) return false;\n if (!isPidAlive(info.pid)) return false;\n\n if (!info.startToken) return true;\n\n const currentToken = captureProcessStartToken(info.pid);\n if (currentToken === null) return true;\n\n const match = currentToken === info.startToken;\n if (!match) {\n logger.debug('SYSTEM', 'verifyPidFileOwnership: start-token mismatch (PID reused)', {\n pid: info.pid,\n stored: info.startToken,\n current: currentToken\n });\n }\n return match;\n}\n\nexport class ProcessRegistry {\n private readonly registryPath: string;\n private readonly entries = new Map<string, ManagedProcessInfo>();\n private readonly runtimeProcesses = new Map<string, ChildProcess>();\n private initialized = false;\n\n constructor(registryPath: string = DEFAULT_REGISTRY_PATH) {\n this.registryPath = registryPath;\n }\n\n initialize(): void {\n if (this.initialized) return;\n this.initialized = true;\n\n mkdirSync(path.dirname(this.registryPath), { recursive: true });\n\n if (!existsSync(this.registryPath)) {\n this.persist();\n return;\n }\n\n try {\n const raw = JSON.parse(readFileSync(this.registryPath, 'utf-8')) as PersistedRegistry;\n const processes = raw.processes ?? {};\n for (const [id, info] of Object.entries(processes)) {\n this.entries.set(id, info);\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.warn('SYSTEM', 'Failed to parse supervisor registry, rebuilding', {\n path: this.registryPath\n }, error);\n } else {\n logger.warn('SYSTEM', 'Failed to parse supervisor registry, rebuilding', {\n path: this.registryPath,\n error: String(error)\n });\n }\n this.entries.clear();\n }\n\n const removed = this.pruneDeadEntries();\n if (removed > 0) {\n logger.info('SYSTEM', 'Removed dead processes from supervisor registry', { removed });\n }\n this.persist();\n }\n\n register(id: string, processInfo: ManagedProcessInfo, processRef?: ChildProcess): void {\n this.initialize();\n this.entries.set(id, processInfo);\n if (processRef) {\n this.runtimeProcesses.set(id, processRef);\n }\n this.persist();\n }\n\n unregister(id: string): void {\n this.initialize();\n const existing = this.entries.get(id);\n this.entries.delete(id);\n this.runtimeProcesses.delete(id);\n this.persist();\n if (existing?.type === 'sdk') notifySlotAvailable();\n }\n\n clear(): void {\n this.entries.clear();\n this.runtimeProcesses.clear();\n this.persist();\n }\n\n getAll(): ManagedProcessRecord[] {\n this.initialize();\n return Array.from(this.entries.entries())\n .map(([id, info]) => ({ id, ...info }))\n .sort((a, b) => {\n const left = Date.parse(a.startedAt);\n const right = Date.parse(b.startedAt);\n return (Number.isNaN(left) ? 0 : left) - (Number.isNaN(right) ? 0 : right);\n });\n }\n\n getBySession(sessionId: string | number): ManagedProcessRecord[] {\n const normalized = String(sessionId);\n return this.getAll().filter(record => record.sessionId !== undefined && String(record.sessionId) === normalized);\n }\n\n getRuntimeProcess(id: string): ChildProcess | undefined {\n return this.runtimeProcesses.get(id);\n }\n\n getByPid(pid: number): ManagedProcessRecord[] {\n return this.getAll().filter(record => record.pid === pid);\n }\n\n pruneDeadEntries(): number {\n this.initialize();\n\n let removed = 0;\n let removedSdk = 0;\n for (const [id, info] of this.entries) {\n if (isPidAlive(info.pid)) continue;\n this.entries.delete(id);\n this.runtimeProcesses.delete(id);\n removed += 1;\n if (info.type === 'sdk') removedSdk += 1;\n }\n\n if (removed > 0) {\n this.persist();\n }\n for (let i = 0; i < removedSdk; i += 1) notifySlotAvailable();\n\n return removed;\n }\n\n async reapSession(sessionId: string | number): Promise<number> {\n this.initialize();\n\n const sessionRecords = this.getBySession(sessionId);\n if (sessionRecords.length === 0) {\n return 0;\n }\n\n const sessionIdNum = typeof sessionId === 'number' ? sessionId : Number(sessionId) || undefined;\n logger.info('SYSTEM', `Reaping ${sessionRecords.length} process(es) for session ${sessionId}`, {\n sessionId: sessionIdNum,\n pids: sessionRecords.map(r => r.pid)\n });\n\n const aliveRecords = sessionRecords.filter(r => isPidAlive(r.pid));\n for (const record of aliveRecords) {\n try {\n if (typeof record.pgid === 'number' && process.platform !== 'win32') {\n process.kill(-record.pgid, 'SIGTERM');\n } else {\n process.kill(record.pid, 'SIGTERM');\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== 'ESRCH') {\n logger.debug('SYSTEM', `Failed to SIGTERM session process PID ${record.pid}`, {\n pid: record.pid,\n pgid: record.pgid\n }, error);\n }\n } else {\n logger.warn('SYSTEM', `Failed to SIGTERM session process PID ${record.pid} (non-Error)`, {\n pid: record.pid,\n pgid: record.pgid,\n error: String(error)\n });\n }\n }\n }\n\n await waitForExit(aliveRecords, REAP_SESSION_SIGTERM_TIMEOUT_MS);\n\n const survivors = aliveRecords.filter(r => isPidAlive(r.pid));\n for (const record of survivors) {\n logger.warn('SYSTEM', `Session process PID ${record.pid} did not exit after SIGTERM, sending SIGKILL`, {\n pid: record.pid,\n pgid: record.pgid,\n sessionId: sessionIdNum\n });\n try {\n if (typeof record.pgid === 'number' && process.platform !== 'win32') {\n process.kill(-record.pgid, 'SIGKILL');\n } else {\n process.kill(record.pid, 'SIGKILL');\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== 'ESRCH') {\n logger.debug('SYSTEM', `Failed to SIGKILL session process PID ${record.pid}`, {\n pid: record.pid,\n pgid: record.pgid\n }, error);\n }\n } else {\n logger.warn('SYSTEM', `Failed to SIGKILL session process PID ${record.pid} (non-Error)`, {\n pid: record.pid,\n pgid: record.pgid,\n error: String(error)\n });\n }\n }\n }\n\n if (survivors.length > 0) {\n const sigkillDeadline = Date.now() + REAP_SESSION_SIGKILL_TIMEOUT_MS;\n while (Date.now() < sigkillDeadline) {\n const remaining = survivors.filter(r => isPidAlive(r.pid));\n if (remaining.length === 0) break;\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n }\n\n for (const record of sessionRecords) {\n this.entries.delete(record.id);\n this.runtimeProcesses.delete(record.id);\n }\n this.persist();\n for (const record of sessionRecords) {\n if (record.type === 'sdk') notifySlotAvailable();\n }\n\n logger.info('SYSTEM', `Reaped ${sessionRecords.length} process(es) for session ${sessionId}`, {\n sessionId: sessionIdNum,\n reaped: sessionRecords.length\n });\n\n return sessionRecords.length;\n }\n\n private persist(): void {\n const payload: PersistedRegistry = {\n processes: Object.fromEntries(this.entries.entries())\n };\n\n mkdirSync(path.dirname(this.registryPath), { recursive: true });\n writeFileSync(this.registryPath, JSON.stringify(payload, null, 2));\n }\n}\n\nlet registrySingleton: ProcessRegistry | null = null;\n\nexport function getProcessRegistry(): ProcessRegistry {\n if (!registrySingleton) {\n registrySingleton = new ProcessRegistry();\n }\n return registrySingleton;\n}\n\nexport function createProcessRegistry(registryPath: string): ProcessRegistry {\n return new ProcessRegistry(registryPath);\n}\n\nexport interface TrackedSdkProcess {\n pid: number;\n pgid: number | undefined;\n sessionDbId: number;\n process: ChildProcess;\n}\n\nexport function getSdkProcessForSession(sessionDbId: number): TrackedSdkProcess | undefined {\n const registry = getProcessRegistry();\n const matches = registry.getBySession(sessionDbId).filter(r => r.type === 'sdk');\n\n if (matches.length > 1) {\n logger.warn('PROCESS', `Multiple SDK processes found for session ${sessionDbId}`, {\n count: matches.length,\n pids: matches.map(m => m.pid),\n });\n }\n\n const record = matches[0];\n if (!record) return undefined;\n\n const processRef = registry.getRuntimeProcess(record.id);\n if (!processRef) return undefined;\n\n return {\n pid: record.pid,\n pgid: record.pgid,\n sessionDbId,\n process: processRef,\n };\n}\n\nexport async function ensureSdkProcessExit(\n tracked: TrackedSdkProcess,\n timeoutMs: number = 5000\n): Promise<void> {\n const { pid, pgid, process: proc } = tracked;\n\n if (proc.exitCode !== null) return;\n\n const exitPromise = new Promise<void>((resolve) => {\n proc.once('exit', () => resolve());\n });\n\n const timeoutPromise = new Promise<void>((resolve) => {\n setTimeout(resolve, timeoutMs);\n });\n\n await Promise.race([exitPromise, timeoutPromise]);\n\n if (proc.exitCode !== null) return;\n\n logger.warn('PROCESS', `PID ${pid} did not exit after ${timeoutMs}ms, sending SIGKILL to process group`, {\n pid, pgid, timeoutMs,\n });\n try {\n if (typeof pgid === 'number' && process.platform !== 'win32') {\n process.kill(-pgid, 'SIGKILL');\n } else {\n proc.kill('SIGKILL');\n }\n } catch {\n // Already dead — fine.\n }\n\n const sigkillExit = new Promise<void>((resolve) => {\n proc.once('exit', () => resolve());\n });\n const sigkillTimeout = new Promise<void>((resolve) => {\n setTimeout(resolve, 1000);\n });\n await Promise.race([sigkillExit, sigkillTimeout]);\n}\n\nconst TOTAL_PROCESS_HARD_CAP = 10;\nconst SLOT_RECHECK_INTERVAL_MS = 5_000;\nconst slotWaiters: Array<() => void> = [];\n\nfunction getActiveSdkCount(): number {\n return getProcessRegistry().getAll().filter(record => record.type === 'sdk').length;\n}\n\nfunction notifySlotAvailable(): void {\n const waiter = slotWaiters.shift();\n if (waiter) waiter();\n}\n\nexport async function waitForSlot(maxConcurrent: number, signal?: AbortSignal): Promise<void> {\n getProcessRegistry().pruneDeadEntries();\n const activeCount = getActiveSdkCount();\n if (activeCount >= TOTAL_PROCESS_HARD_CAP) {\n throw new Error(`Hard cap exceeded: ${activeCount} processes in registry (cap=${TOTAL_PROCESS_HARD_CAP}). Refusing to spawn more.`);\n }\n\n if (activeCount < maxConcurrent) return;\n\n if (signal?.aborted) {\n throw new Error('waitForSlot aborted before queuing');\n }\n\n logger.info('PROCESS', `Pool limit reached (${activeCount}/${maxConcurrent}), waiting for slot...`);\n\n return new Promise<void>((resolve, reject) => {\n let recheckTimer: ReturnType<typeof setInterval> | null = null;\n let abortHandler: (() => void) | null = null;\n const cleanup = () => {\n if (recheckTimer) clearInterval(recheckTimer);\n if (abortHandler && signal) signal.removeEventListener('abort', abortHandler);\n const idx = slotWaiters.indexOf(onSlot);\n if (idx >= 0) slotWaiters.splice(idx, 1);\n };\n const onSlot = () => {\n const count = getActiveSdkCount();\n if (count >= TOTAL_PROCESS_HARD_CAP) {\n cleanup();\n reject(new Error(`Hard cap exceeded: ${count} processes in registry (cap=${TOTAL_PROCESS_HARD_CAP}). Refusing to spawn more.`));\n return;\n }\n\n if (count < maxConcurrent) {\n cleanup();\n resolve();\n } else {\n slotWaiters.push(onSlot);\n }\n };\n\n if (signal) {\n abortHandler = () => {\n cleanup();\n reject(new Error('waitForSlot aborted'));\n };\n signal.addEventListener('abort', abortHandler, { once: true });\n }\n\n slotWaiters.push(onSlot);\n recheckTimer = setInterval(() => {\n const removed = getProcessRegistry().pruneDeadEntries();\n if (removed > 0) {\n logger.info('PROCESS', 'Pruned stale process registry entries while waiting for agent slot', { removed });\n return;\n }\n notifySlotAvailable();\n }, SLOT_RECHECK_INTERVAL_MS);\n recheckTimer.unref?.();\n });\n}\n\nexport interface SpawnedSdkProcess {\n stdin: NonNullable<ChildProcess['stdin']>;\n stdout: NonNullable<ChildProcess['stdout']>;\n stderr: NonNullable<ChildProcess['stderr']>;\n readonly killed: boolean;\n readonly exitCode: number | null;\n kill: ChildProcess['kill'];\n on: ChildProcess['on'];\n once: ChildProcess['once'];\n off: ChildProcess['off'];\n}\n\nexport interface SpawnSdkOptions {\n command: string;\n args: string[];\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n signal?: AbortSignal;\n}\n\nexport function spawnSdkProcess(\n sessionDbId: number,\n options: SpawnSdkOptions\n): { process: SpawnedSdkProcess; pid: number; pgid: number } | null {\n const registry = getProcessRegistry();\n\n const useCmdWrapper = process.platform === 'win32' && options.command.endsWith('.cmd');\n const env = sanitizeEnv(options.env ?? process.env);\n\n const filteredArgs: string[] = [];\n for (const arg of options.args) {\n if (arg === '') {\n if (filteredArgs.length > 0 && filteredArgs[filteredArgs.length - 1].startsWith('--')) {\n filteredArgs.pop();\n }\n continue;\n }\n filteredArgs.push(arg);\n }\n\n const isWin = process.platform === 'win32';\n const child = useCmdWrapper\n ? spawnHidden('cmd.exe', ['/d', '/c', options.command, ...filteredArgs], {\n cwd: options.cwd,\n env,\n detached: !isWin,\n stdio: ['pipe', 'pipe', 'pipe'],\n signal: options.signal,\n windowsHide: true,\n })\n : spawnHidden(options.command, filteredArgs, {\n cwd: options.cwd,\n env,\n detached: !isWin,\n stdio: ['pipe', 'pipe', 'pipe'],\n signal: options.signal,\n windowsHide: true,\n });\n\n child.on('error', (err: Error) => {\n logger.warn('SDK_SPAWN', `[session-${sessionDbId}] child emitted error event`, {\n sessionDbId,\n pid: child.pid,\n errorName: err.name,\n errorCode: (err as NodeJS.ErrnoException).code,\n }, err);\n });\n\n if (!child.pid) {\n logger.error('PROCESS', 'Spawn succeeded but produced no PID', { sessionDbId });\n return null;\n }\n\n const pid = child.pid;\n const pgid = pid; \n\n // Keep the tail of stderr so a non-zero exit can say WHY at WARN level.\n // Without this, a CLI that dies at flag parsing (\"error: unknown option…\")\n // logs only an opaque {code=1} and the real cause is invisible unless the\n // worker happens to run at DEBUG.\n const STDERR_TAIL_MAX_CHARS = 2048;\n let stderrTail = '';\n if (child.stderr) {\n child.stderr.on('data', (data: Buffer) => {\n const text = data.toString();\n stderrTail = (stderrTail + text).slice(-STDERR_TAIL_MAX_CHARS);\n logger.debug('SDK_SPAWN', `[session-${sessionDbId}] stderr: ${text.trim()}`);\n });\n }\n\n const recordId = `sdk:${sessionDbId}:${pid}`;\n registry.register(recordId, {\n pid,\n type: 'sdk',\n sessionId: sessionDbId,\n startedAt: new Date().toISOString(),\n pgid,\n }, child);\n\n child.on('exit', () => {\n registry.unregister(recordId);\n });\n\n // 'close', not 'exit': 'exit' can fire while piped stderr still holds\n // buffered data, truncating the tail. 'close' waits for all stdio to drain.\n child.on('close', (code: number | null, signal: string | null) => {\n if (code !== 0) {\n const tail = stderrTail.trim();\n logger.warn('SDK_SPAWN', `[session-${sessionDbId}] Claude process exited`, {\n code,\n signal,\n pid,\n ...(tail ? { stderrTail: tail } : {}),\n });\n }\n });\n\n if (!child.stdin || !child.stdout || !child.stderr) {\n logger.error('PROCESS', 'Spawned SDK child missing required stdio streams', {\n sessionDbId,\n pid,\n hasStdin: Boolean(child.stdin),\n hasStdout: Boolean(child.stdout),\n hasStderr: Boolean(child.stderr),\n });\n try { child.kill('SIGKILL'); } catch { /* already dead */ }\n return null;\n }\n\n const spawned: SpawnedSdkProcess = {\n stdin: child.stdin,\n stdout: child.stdout,\n stderr: child.stderr,\n get killed() { return child.killed; },\n get exitCode() { return child.exitCode; },\n kill: child.kill.bind(child),\n on: child.on.bind(child),\n once: child.once.bind(child),\n off: child.off.bind(child),\n };\n\n return { process: spawned, pid, pgid };\n}\n\nexport function createSdkSpawnFactory(sessionDbId: number) {\n return (spawnOptions: SpawnSdkOptions): SpawnedSdkProcess => {\n const registry = getProcessRegistry();\n\n const existing = registry.getBySession(sessionDbId).filter(r => r.type === 'sdk');\n for (const record of existing) {\n if (!isPidAlive(record.pid)) continue;\n try {\n if (typeof record.pgid === 'number') {\n if (process.platform !== 'win32') {\n process.kill(-record.pgid, 'SIGTERM');\n } else {\n process.kill(record.pid, 'SIGTERM');\n }\n } else {\n process.kill(record.pid, 'SIGTERM');\n }\n logger.warn('PROCESS', `Killing duplicate SDK process PID ${record.pid} before spawning new one for session ${sessionDbId}`, {\n existingPid: record.pid,\n sessionDbId,\n });\n } catch (error: unknown) {\n const code = error instanceof Error ? (error as NodeJS.ErrnoException).code : undefined;\n if (code !== 'ESRCH') {\n if (error instanceof Error) {\n logger.warn('PROCESS', `Failed to SIGTERM duplicate SDK process PID ${record.pid}`, { sessionDbId }, error);\n } else {\n logger.warn('PROCESS', `Failed to SIGTERM duplicate SDK process PID ${record.pid} (non-Error)`, {\n sessionDbId, error: String(error),\n });\n }\n }\n }\n }\n\n const result = spawnSdkProcess(sessionDbId, spawnOptions);\n if (!result) {\n throw new Error(`Failed to spawn SDK subprocess for session ${sessionDbId}`);\n }\n\n return result.process;\n };\n}\n","// F1 foundation: spawn wrapper that hides child windows on Windows by default. See src/shared/spawn.ts.test.ts for invariant.\nimport { spawn, type SpawnOptions, type ChildProcess } from 'node:child_process';\n\nexport type SpawnHiddenOptions = SpawnOptions;\n\nexport function spawnHidden(\n command: string,\n args?: readonly string[],\n options?: SpawnOptions\n): ChildProcess {\n return spawn(command, args ?? [], { windowsHide: true, ...options });\n}\n","import { execFile } from 'child_process';\nimport { existsSync, readFileSync, rmSync } from 'fs';\nimport { promisify } from 'util';\nimport { logger } from '../utils/logger.js';\nimport { HOOK_TIMEOUTS } from '../shared/hook-constants.js';\nimport { isPidAlive, waitForExit, type ManagedProcessRecord, type ProcessRegistry } from './process-registry.js';\nimport { paths } from '../shared/paths.js';\n\nconst execFileAsync = promisify(execFile);\nconst PID_FILE = paths.workerPid();\n\ntype TreeKillFn = (pid: number, signal?: string, callback?: (error?: Error | null) => void) => void;\n\nexport interface ShutdownCascadeOptions {\n registry: ProcessRegistry;\n currentPid?: number;\n pidFilePath?: string;\n}\n\nexport async function runShutdownCascade(options: ShutdownCascadeOptions): Promise<void> {\n const currentPid = options.currentPid ?? process.pid;\n const pidFilePath = options.pidFilePath ?? PID_FILE;\n const allRecords = options.registry.getAll();\n const childRecords = [...allRecords]\n .filter(record => record.pid !== currentPid)\n .sort((a, b) => Date.parse(b.startedAt) - Date.parse(a.startedAt));\n\n for (const record of childRecords) {\n if (!isPidAlive(record.pid)) {\n options.registry.unregister(record.id);\n continue;\n }\n\n try {\n await signalProcess(record, 'SIGTERM');\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.debug('SYSTEM', 'Failed to send SIGTERM to child process', {\n pid: record.pid,\n pgid: record.pgid,\n type: record.type\n }, error);\n } else {\n logger.warn('SYSTEM', 'Failed to send SIGTERM to child process (non-Error)', {\n pid: record.pid,\n pgid: record.pgid,\n type: record.type,\n error: String(error)\n });\n }\n }\n }\n\n await waitForExit(childRecords, 5000);\n\n const survivors = childRecords.filter(record => isPidAlive(record.pid));\n for (const record of survivors) {\n try {\n await signalProcess(record, 'SIGKILL');\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.debug('SYSTEM', 'Failed to force kill child process', {\n pid: record.pid,\n pgid: record.pgid,\n type: record.type\n }, error);\n } else {\n logger.warn('SYSTEM', 'Failed to force kill child process (non-Error)', {\n pid: record.pid,\n pgid: record.pgid,\n type: record.type,\n error: String(error)\n });\n }\n }\n }\n\n await waitForExit(survivors, 1000);\n\n for (const record of childRecords) {\n options.registry.unregister(record.id);\n }\n for (const record of allRecords.filter(record => record.pid === currentPid)) {\n options.registry.unregister(record.id);\n }\n\n removeOwnedPidFile(pidFilePath, currentPid);\n\n options.registry.pruneDeadEntries();\n}\n\n/**\n * Owner-guarded PID-file removal (Phase 5, worker-restart plan).\n *\n * The shutdown cascade is the dying worker's LAST act — during a restart the\n * successor worker has typically already written its OWN PID file by the time\n * this runs. Blindly rmSync'ing here clobbered that file and made\n * `worker status` report a healthy worker as not running. Deletion therefore\n * requires proof of ownership: the recorded pid must equal `currentPid`.\n *\n * A missing file is a no-op. An unreadable/corrupt file cannot prove\n * ownership, so it is left in place (the safe default): readPidFile() and\n * validateWorkerPidFile() both treat unparseable files as ownerless, so a\n * leftover corrupt file never blocks a successor's boot and is cleaned up by\n * the next worker start.\n */\nexport function removeOwnedPidFile(pidFilePath: string, currentPid: number): void {\n if (!existsSync(pidFilePath)) return;\n\n let recordedPid: number | null = null;\n try {\n const parsed = JSON.parse(readFileSync(pidFilePath, 'utf-8')) as { pid?: unknown };\n recordedPid = typeof parsed.pid === 'number' ? parsed.pid : null;\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'PID file unreadable during shutdown — leaving it (cannot prove ownership)', {\n pidFilePath,\n error: error instanceof Error ? error.message : String(error)\n });\n return;\n }\n\n if (recordedPid !== currentPid) {\n logger.debug('SYSTEM', 'PID file not owned by this process — leaving it for its owner (restart successor?)', {\n pidFilePath,\n recordedPid,\n currentPid\n });\n return;\n }\n\n try {\n rmSync(pidFilePath, { force: true });\n } catch (error: unknown) {\n if (error instanceof Error) {\n logger.debug('SYSTEM', 'Failed to remove PID file during shutdown', { pidFilePath }, error);\n } else {\n logger.warn('SYSTEM', 'Failed to remove PID file during shutdown (non-Error)', {\n pidFilePath,\n error: String(error)\n });\n }\n }\n}\n\nasync function signalProcess(record: ManagedProcessRecord, signal: 'SIGTERM' | 'SIGKILL'): Promise<void> {\n const { pid, pgid } = record;\n\n if (process.platform !== 'win32') {\n // Try the process group first when we have one — it reaches grandchildren\n // re-parented to init. If the group is already gone (ESRCH) the actual\n // root pid may still be alive (e.g. it survived its own group teardown);\n // fall through to the per-pid signal so shutdown isn't a no-op\n // (CodeRabbit review on PR #2282).\n if (typeof pgid === 'number') {\n try {\n process.kill(-pgid, signal);\n return;\n } catch (error: unknown) {\n const errno = error instanceof Error ? (error as NodeJS.ErrnoException).code : undefined;\n if (errno !== 'ESRCH') {\n throw error;\n }\n // ESRCH on the group — fall through and try the bare pid below.\n }\n }\n\n try {\n process.kill(pid, signal);\n } catch (error: unknown) {\n const errno = error instanceof Error ? (error as NodeJS.ErrnoException).code : undefined;\n if (errno !== 'ESRCH') {\n throw error;\n }\n }\n return;\n }\n\n if (signal === 'SIGTERM') {\n try {\n process.kill(pid, signal);\n } catch (error: unknown) {\n if (error instanceof Error) {\n const errno = (error as NodeJS.ErrnoException).code;\n if (errno === 'ESRCH') {\n return;\n }\n }\n throw error;\n }\n return;\n }\n\n const treeKill = await loadTreeKill();\n if (treeKill) {\n await new Promise<void>((resolve, reject) => {\n treeKill(pid, signal, (error) => {\n if (!error) {\n resolve();\n return;\n }\n\n const errno = (error as NodeJS.ErrnoException).code;\n if (errno === 'ESRCH') {\n resolve();\n return;\n }\n reject(error);\n });\n });\n return;\n }\n\n const args = ['/PID', String(pid), '/T'];\n if (signal === 'SIGKILL') {\n args.push('/F');\n }\n\n await execFileAsync('taskkill', args, {\n timeout: HOOK_TIMEOUTS.POWERSHELL_COMMAND,\n windowsHide: true\n });\n}\n\nasync function loadTreeKill(): Promise<TreeKillFn | null> {\n const moduleName = 'tree-kill';\n\n try {\n const treeKillModule = await import(moduleName);\n return (treeKillModule.default ?? treeKillModule) as TreeKillFn;\n } catch (error: unknown) {\n logger.debug('SYSTEM', 'tree-kill module not available, using fallback', {}, error instanceof Error ? error : undefined);\n return null;\n }\n}\n","\nimport { logger } from '../utils/logger.js';\nimport { getProcessRegistry } from './process-registry.js';\n\nconst HEALTH_CHECK_INTERVAL_MS = 30_000;\n\nlet healthCheckInterval: ReturnType<typeof setInterval> | null = null;\n\nfunction runHealthCheck(): void {\n const registry = getProcessRegistry();\n\n const removedProcessCount = registry.pruneDeadEntries();\n if (removedProcessCount > 0) {\n logger.info('SYSTEM', `Health check: pruned ${removedProcessCount} dead process(es) from registry`);\n }\n}\n\nexport function startHealthChecker(): void {\n if (healthCheckInterval !== null) return;\n\n healthCheckInterval = setInterval(runHealthCheck, HEALTH_CHECK_INTERVAL_MS);\n healthCheckInterval.unref();\n\n logger.debug('SYSTEM', 'Health checker started', { intervalMs: HEALTH_CHECK_INTERVAL_MS });\n}\n\nexport function stopHealthChecker(): void {\n if (healthCheckInterval === null) return;\n\n clearInterval(healthCheckInterval);\n healthCheckInterval = null;\n\n logger.debug('SYSTEM', 'Health checker stopped');\n}\n","export type DependencyStatusKind =\n | 'ok'\n | 'setup_required'\n | 'vector_search_unavailable';\n\nexport type DependencyName = 'claude_cli' | 'uvx';\n\nexport interface DependencyStatus {\n dependency: DependencyName;\n kind: DependencyStatusKind;\n message: string;\n remediation?: string;\n recordedAtMs: number;\n}\n\nexport const CLAUDE_CLI_SETUP_RECHECK_COOLDOWN_MS = 30_000;\n\nexport const CLAUDE_CLI_SETUP_REMEDIATION =\n 'Install or update Claude Code CLI, then restart claude-mem. Try `claude update`, ' +\n '`npm install -g @anthropic-ai/claude-code@latest`, or set CLAUDE_CODE_PATH in ~/.claude-mem/settings.json.';\n\nexport const UVX_VECTOR_SEARCH_REMEDIATION =\n 'Install uv/uvx and make uvx visible to the worker PATH, then restart claude-mem. ' +\n 'Try `curl -LsSf https://astral.sh/uv/install.sh | sh` or `brew install uv`.';\n\nconst statuses = new Map<DependencyName, DependencyStatus>();\n\nexport interface DependencyHealthSnapshot {\n degraded: boolean;\n statuses: DependencyStatus[];\n}\n\nexport function recordDependencyStatus(\n dependency: DependencyName,\n kind: Exclude<DependencyStatusKind, 'ok'>,\n message: string,\n remediation?: string,\n): DependencyStatus {\n const status: DependencyStatus = {\n dependency,\n kind,\n message,\n ...(remediation ? { remediation } : {}),\n recordedAtMs: Date.now(),\n };\n statuses.set(dependency, status);\n return status;\n}\n\nexport function recordClaudeCliSetupRequired(message: string): DependencyStatus {\n return recordDependencyStatus('claude_cli', 'setup_required', message, CLAUDE_CLI_SETUP_REMEDIATION);\n}\n\nexport function recordUvxVectorSearchUnavailable(message: string): DependencyStatus {\n return recordDependencyStatus('uvx', 'vector_search_unavailable', message, UVX_VECTOR_SEARCH_REMEDIATION);\n}\n\nexport function clearDependencyStatus(dependency: DependencyName): void {\n statuses.delete(dependency);\n}\n\nexport function getDependencyStatus(dependency: DependencyName): DependencyStatus | null {\n return statuses.get(dependency) ?? null;\n}\n\nexport function isDependencyBlocked(\n dependency: DependencyName,\n kind?: Exclude<DependencyStatusKind, 'ok'>,\n): boolean {\n const status = getDependencyStatus(dependency);\n if (!status) return false;\n return kind ? status.kind === kind : status.kind !== 'ok';\n}\n\nexport function isDependencyStatusInCooldown(\n status: DependencyStatus,\n cooldownMs: number,\n nowMs: number = Date.now(),\n): boolean {\n return nowMs - status.recordedAtMs < cooldownMs;\n}\n\nexport function snapshotDependencyHealth(): DependencyHealthSnapshot {\n const currentStatuses = Array.from(statuses.values())\n .map(status => ({ ...status }))\n .sort((a, b) => a.dependency.localeCompare(b.dependency));\n return {\n degraded: currentStatuses.some(status => status.kind !== 'ok'),\n statuses: currentStatuses,\n };\n}\n\nexport function resetDependencyStatusesForTesting(): void {\n statuses.clear();\n}\n","\nimport { Request, Response, NextFunction, ErrorRequestHandler } from 'express';\nimport { logger } from '../../utils/logger.js';\n\nexport interface ErrorResponse {\n error: string;\n message: string;\n code?: string;\n details?: unknown;\n}\n\nexport class AppError extends Error {\n constructor(\n message: string,\n public statusCode: number = 500,\n public code?: string,\n public details?: unknown\n ) {\n super(message);\n this.name = 'AppError';\n }\n}\n\nexport function createErrorResponse(\n error: string,\n message: string,\n code?: string,\n details?: unknown\n): ErrorResponse {\n const response: ErrorResponse = { error, message };\n if (code) response.code = code;\n if (details) response.details = details;\n return response;\n}\n\nexport const errorHandler: ErrorRequestHandler = (\n err: Error | AppError,\n req: Request,\n res: Response,\n _next: NextFunction\n): void => {\n const statusCode = err instanceof AppError ? err.statusCode : 500;\n\n logger.error('HTTP', `Error handling ${req.method} ${req.path}`, {\n statusCode,\n error: err.message,\n code: err instanceof AppError ? err.code : undefined\n }, err);\n\n const response = createErrorResponse(\n err.name || 'Error',\n err.message,\n err instanceof AppError ? err.code : undefined,\n err instanceof AppError ? err.details : undefined\n );\n\n res.status(statusCode).json(response);\n};\n\nexport function notFoundHandler(req: Request, res: Response): void {\n res.status(404).json(createErrorResponse(\n 'NotFound',\n `Cannot ${req.method} ${req.path}`\n ));\n}\n","\nimport { AppError } from '../../server/ErrorHandler.js';\n\nexport class ChromaUnavailableError extends AppError {\n constructor(message: string, cause?: Error) {\n super(message, 503, 'CHROMA_UNAVAILABLE', cause ? { cause: cause.message } : undefined);\n this.name = 'ChromaUnavailableError';\n }\n}\n","import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { SettingsDefaultsManager } from '../../shared/SettingsDefaultsManager.js';\nimport { logger } from '../../utils/logger.js';\n\ntype DocKind = 'observations' | 'summaries' | 'prompts';\n\nexport interface ProjectWatermarks {\n observations: number;\n summaries: number;\n prompts: number;\n}\n\nconst ZERO: ProjectWatermarks = { observations: 0, summaries: 0, prompts: 0 };\n\nfunction statePath(): string {\n const dataDir = SettingsDefaultsManager.get('CLAUDE_MEM_DATA_DIR');\n return join(dataDir, 'chroma-sync-state.json');\n}\n\nlet cache: Record<string, ProjectWatermarks> | null = null;\n\nfunction load(): Record<string, ProjectWatermarks> {\n if (cache) return cache;\n const path = statePath();\n if (!existsSync(path)) {\n cache = {};\n return cache;\n }\n const raw = readFileSync(path, 'utf8');\n const parsed = JSON.parse(raw) as Record<string, Partial<ProjectWatermarks>>;\n const normalized: Record<string, ProjectWatermarks> = {};\n for (const [project, marks] of Object.entries(parsed)) {\n normalized[project] = {\n observations: Number.isInteger(marks.observations) ? marks.observations as number : 0,\n summaries: Number.isInteger(marks.summaries) ? marks.summaries as number : 0,\n prompts: Number.isInteger(marks.prompts) ? marks.prompts as number : 0\n };\n }\n cache = normalized;\n return cache;\n}\n\nfunction persist(): void {\n if (!cache) return;\n const path = statePath();\n const dataDir = SettingsDefaultsManager.get('CLAUDE_MEM_DATA_DIR');\n if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true });\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, JSON.stringify(cache, null, 2), 'utf8');\n renameSync(tmp, path);\n}\n\nexport const ChromaSyncState = {\n exists(): boolean {\n return existsSync(statePath());\n },\n\n get(project: string): ProjectWatermarks {\n const all = load();\n return { ...(all[project] ?? ZERO) };\n },\n\n bump(project: string, kind: DocKind, id: number): void {\n if (!Number.isInteger(id) || id <= 0) return;\n const all = load();\n const current = all[project] ?? { ...ZERO };\n if (id <= current[kind]) return;\n current[kind] = id;\n all[project] = current;\n persist();\n },\n\n replace(project: string, marks: ProjectWatermarks): void {\n const all = load();\n all[project] = { ...marks };\n persist();\n }\n};\n","\nimport { ChromaMcpManager } from './ChromaMcpManager.js';\nimport { ChromaSyncState, ProjectWatermarks } from './ChromaSyncState.js';\nimport { ParsedObservation, ParsedSummary } from '../../sdk/parser.js';\n// cmem-sdk: keep SessionStore + parseFileList off the SDK's import graph.\n// Both come from the SQLite layer (`bun:sqlite`). The SDK only uses the\n// constructor + ensureCollectionExists + close() surface of ChromaSync,\n// so a TYPE-ONLY import is sufficient — value-level uses (`new\n// SessionStore()` / parseFileList(...)) are loaded lazily inside the\n// SQLite-only methods that need them. Plan §3 anti-pattern: do NOT add\n// `bun:sqlite` to the SDK bundle externals — fix the import chain.\nimport type { SessionStore as SessionStoreType } from '../sqlite/SessionStore.js';\nimport { logger } from '../../utils/logger.js';\nimport { ChromaUnavailableError } from '../worker/search/errors.js';\nimport { normalizePlatformSource } from '../../shared/platform-source.js';\nimport type * as SqliteFilesModule from '../sqlite/observations/files.js';\n\ntype SessionStore = SessionStoreType;\ntype SessionStoreCtor = new () => SessionStoreType;\n\n// Lazy CJS require so tsup (used by the cmem-sdk build) does not follow\n// these SQLite-coupled modules into the SDK bundle. Worker/Bun runtime\n// reaches them at first call; the SDK never calls the methods that\n// trigger these loads, so they never load in SDK consumers.\nconst lazyCreateRequire = (): ((id: string) => unknown) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mod = require('module') as typeof import('module');\n return mod.createRequire(import.meta.url);\n};\n\nlet _sessionStoreCtor: SessionStoreCtor | undefined;\nfunction loadSessionStoreCtor(): SessionStoreCtor {\n if (!_sessionStoreCtor) {\n const req = lazyCreateRequire();\n const m = req('../sqlite/SessionStore.js') as { SessionStore: SessionStoreCtor };\n _sessionStoreCtor = m.SessionStore;\n }\n return _sessionStoreCtor;\n}\n\nlet _filesHelper: typeof SqliteFilesModule | undefined;\nfunction loadFilesHelper(): typeof SqliteFilesModule {\n if (!_filesHelper) {\n const req = lazyCreateRequire();\n _filesHelper = req('../sqlite/observations/files.js') as typeof SqliteFilesModule;\n }\n return _filesHelper;\n}\n\n// Exported for cmem-sdk Phase 6: the SDK builds ChromaDocument values from\n// Postgres observations (UUID id, content string, metadata bag) and calls\n// the now-public addDocuments() to index them. Shape is unchanged.\nexport interface ChromaDocument {\n id: string;\n document: string;\n metadata: Record<string, string | number>;\n}\n\ninterface StoredObservation {\n id: number;\n memory_session_id: string;\n project: string;\n merged_into_project: string | null;\n platform_source?: string | null;\n text: string | null;\n type: string;\n title: string | null;\n subtitle: string | null;\n facts: string | null; \n narrative: string | null;\n concepts: string | null; \n files_read: string | null;\n files_modified: string | null;\n prompt_number: number;\n created_at_epoch: number;\n}\n\ninterface StoredSummary {\n id: number;\n memory_session_id: string;\n project: string;\n merged_into_project: string | null;\n platform_source?: string | null;\n request: string | null;\n investigated: string | null;\n learned: string | null;\n completed: string | null;\n next_steps: string | null;\n notes: string | null;\n prompt_number: number;\n created_at_epoch: number;\n}\n\ninterface StoredUserPrompt {\n id: number;\n content_session_id: string;\n prompt_number: number;\n prompt_text: string;\n created_at_epoch: number;\n memory_session_id: string;\n project: string;\n platform_source: string;\n}\n\nexport class ChromaSync {\n private project: string;\n private collectionName: string;\n private collectionCreated = false;\n private readonly BATCH_SIZE = 100;\n\n constructor(project: string) {\n this.project = project;\n const sanitized = project\n .replace(/[^a-zA-Z0-9._-]/g, '_')\n .replace(/[^a-zA-Z0-9]+$/, ''); \n this.collectionName = `cm__${sanitized || 'unknown'}`;\n }\n\n /** Public: cmem-sdk reuses the per-tenant collection name for raw queries. */\n public getCollectionName(): string {\n return this.collectionName;\n }\n\n // Public: cmem-sdk requires Chroma at construction. Plan §3 line 192.\n public async ensureCollectionExists(): Promise<void> {\n if (this.collectionCreated) {\n return;\n }\n\n const chromaMcp = ChromaMcpManager.getInstance();\n try {\n await chromaMcp.callTool('chroma_create_collection', {\n collection_name: this.collectionName\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (!message.includes('already exists')) {\n throw error;\n }\n // Collection already exists - this is the expected path after first creation\n }\n\n this.collectionCreated = true;\n\n logger.debug('CHROMA_SYNC', 'Collection ready', {\n collection: this.collectionName\n });\n }\n\n private formatObservationDocs(obs: StoredObservation): ChromaDocument[] {\n const documents: ChromaDocument[] = [];\n\n const facts = obs.facts ? JSON.parse(obs.facts) : [];\n const concepts = obs.concepts ? JSON.parse(obs.concepts) : [];\n // parseFileList is SQLite-shaped (`bun:sqlite` in the import chain) —\n // resolve it through the deferred loader so this method stays out of\n // the SDK bundle's import graph. Plan §3.\n const filesHelper = loadFilesHelper();\n const files_read = filesHelper.parseFileList(obs.files_read);\n const files_modified = filesHelper.parseFileList(obs.files_modified);\n\n const baseMetadata: Record<string, string | number | null> = {\n sqlite_id: obs.id,\n doc_type: 'observation',\n memory_session_id: obs.memory_session_id,\n project: obs.project,\n merged_into_project: obs.merged_into_project ?? null,\n platform_source: obs.platform_source\n ? normalizePlatformSource(obs.platform_source)\n : normalizePlatformSource(undefined),\n created_at_epoch: obs.created_at_epoch,\n type: obs.type || 'discovery',\n title: obs.title || 'Untitled'\n };\n\n if (obs.subtitle) {\n baseMetadata.subtitle = obs.subtitle;\n }\n if (concepts.length > 0) {\n baseMetadata.concepts = concepts.join(',');\n }\n if (files_read.length > 0) {\n baseMetadata.files_read = files_read.join(',');\n }\n if (files_modified.length > 0) {\n baseMetadata.files_modified = files_modified.join(',');\n }\n\n if (obs.narrative) {\n documents.push({\n id: `obs_${obs.id}_narrative`,\n document: obs.narrative,\n metadata: { ...baseMetadata, field_type: 'narrative' }\n });\n }\n\n if (obs.text) {\n documents.push({\n id: `obs_${obs.id}_text`,\n document: obs.text,\n metadata: { ...baseMetadata, field_type: 'text' }\n });\n }\n\n facts.forEach((fact: string, index: number) => {\n documents.push({\n id: `obs_${obs.id}_fact_${index}`,\n document: fact,\n metadata: { ...baseMetadata, field_type: 'fact', fact_index: index }\n });\n });\n\n return documents;\n }\n\n private formatSummaryDocs(summary: StoredSummary): ChromaDocument[] {\n const documents: ChromaDocument[] = [];\n\n const baseMetadata: Record<string, string | number | null> = {\n sqlite_id: summary.id,\n doc_type: 'session_summary',\n memory_session_id: summary.memory_session_id,\n project: summary.project,\n merged_into_project: summary.merged_into_project ?? null,\n platform_source: summary.platform_source\n ? normalizePlatformSource(summary.platform_source)\n : normalizePlatformSource(undefined),\n created_at_epoch: summary.created_at_epoch,\n prompt_number: summary.prompt_number || 0\n };\n\n if (summary.request) {\n documents.push({\n id: `summary_${summary.id}_request`,\n document: summary.request,\n metadata: { ...baseMetadata, field_type: 'request' }\n });\n }\n\n if (summary.investigated) {\n documents.push({\n id: `summary_${summary.id}_investigated`,\n document: summary.investigated,\n metadata: { ...baseMetadata, field_type: 'investigated' }\n });\n }\n\n if (summary.learned) {\n documents.push({\n id: `summary_${summary.id}_learned`,\n document: summary.learned,\n metadata: { ...baseMetadata, field_type: 'learned' }\n });\n }\n\n if (summary.completed) {\n documents.push({\n id: `summary_${summary.id}_completed`,\n document: summary.completed,\n metadata: { ...baseMetadata, field_type: 'completed' }\n });\n }\n\n if (summary.next_steps) {\n documents.push({\n id: `summary_${summary.id}_next_steps`,\n document: summary.next_steps,\n metadata: { ...baseMetadata, field_type: 'next_steps' }\n });\n }\n\n if (summary.notes) {\n documents.push({\n id: `summary_${summary.id}_notes`,\n document: summary.notes,\n metadata: { ...baseMetadata, field_type: 'notes' }\n });\n }\n\n return documents;\n }\n\n /**\n * Write `documents` to Chroma in BATCH_SIZE-sized batches.\n *\n * Returns the number of documents that were successfully written (or\n * confirmed via delete+add reconcile). Per-batch failures are logged and the\n * loop continues — we never throw — so callers must use the returned count\n * to advance their watermark, otherwise an interrupted backfill can mark\n * unsynced records as synced.\n *\n * Visibility: promoted from `private` to `public` for cmem-sdk Phase 6.\n * The SDK indexes Postgres observations into Chroma using this same\n * storage-agnostic document layer — same retry/dedupe semantics, same\n * BATCH_SIZE. SQLite-shaped `syncObservation` is NOT reusable for the\n * Postgres UUID path. See plan §6 line 244-247.\n */\n public async addDocuments(documents: ChromaDocument[]): Promise<number> {\n if (documents.length === 0) {\n return 0;\n }\n\n try {\n await this.ensureCollectionExists();\n } catch (error) {\n if (error instanceof ChromaUnavailableError) {\n logger.warn('CHROMA_SYNC', 'Chroma unavailable before write; leaving documents unsynced', {\n collection: this.collectionName,\n requested: documents.length,\n error: error.message\n });\n return 0;\n }\n throw error;\n }\n\n const chromaMcp = ChromaMcpManager.getInstance();\n\n let written = 0;\n for (let i = 0; i < documents.length; i += this.BATCH_SIZE) {\n const batch = documents.slice(i, i + this.BATCH_SIZE);\n\n const cleanMetadatas = batch.map(d =>\n Object.fromEntries(\n Object.entries(d.metadata).filter(([_, v]) => v !== null && v !== undefined && v !== '')\n )\n );\n\n try {\n await chromaMcp.callTool('chroma_add_documents', {\n collection_name: this.collectionName,\n ids: batch.map(d => d.id),\n documents: batch.map(d => d.document),\n metadatas: cleanMetadatas\n });\n written += batch.length;\n } catch (error) {\n const errMsg = error instanceof Error ? error.message : String(error);\n if (errMsg.includes('already exist')) {\n try {\n await chromaMcp.callTool('chroma_delete_documents', {\n collection_name: this.collectionName,\n ids: batch.map(d => d.id)\n });\n await chromaMcp.callTool('chroma_add_documents', {\n collection_name: this.collectionName,\n ids: batch.map(d => d.id),\n documents: batch.map(d => d.document),\n metadatas: cleanMetadatas\n });\n written += batch.length;\n logger.info('CHROMA_SYNC', 'Batch reconciled via delete+add after duplicate conflict', {\n collection: this.collectionName,\n batchStart: i,\n batchSize: batch.length\n });\n } catch (reconcileError) {\n logger.error('CHROMA_SYNC', 'Batch reconcile (delete+add) failed — watermark will not advance for this batch', {\n collection: this.collectionName,\n batchStart: i,\n batchSize: batch.length\n }, reconcileError as Error);\n }\n } else {\n logger.error('CHROMA_SYNC', 'Batch add failed — watermark will not advance for this batch, continuing with remaining batches', {\n collection: this.collectionName,\n batchStart: i,\n batchSize: batch.length\n }, error as Error);\n }\n }\n }\n\n logger.debug('CHROMA_SYNC', 'Documents added', {\n collection: this.collectionName,\n requested: documents.length,\n written\n });\n return written;\n }\n\n async syncObservation(\n observationId: number,\n memorySessionId: string,\n project: string,\n obs: ParsedObservation,\n promptNumber: number,\n createdAtEpoch: number,\n platformSource?: string\n ): Promise<void> {\n const stored: StoredObservation = {\n id: observationId,\n memory_session_id: memorySessionId,\n project: project,\n merged_into_project: null,\n platform_source: platformSource ? normalizePlatformSource(platformSource) : normalizePlatformSource(undefined),\n text: null, // Legacy field, not used\n type: obs.type,\n title: obs.title,\n subtitle: obs.subtitle,\n facts: JSON.stringify(obs.facts),\n narrative: obs.narrative,\n concepts: JSON.stringify(obs.concepts),\n files_read: JSON.stringify(obs.files_read),\n files_modified: JSON.stringify(obs.files_modified),\n prompt_number: promptNumber,\n created_at_epoch: createdAtEpoch\n };\n\n const documents = this.formatObservationDocs(stored);\n\n logger.info('CHROMA_SYNC', 'Syncing observation', {\n observationId,\n documentCount: documents.length,\n project\n });\n\n // Only advance the watermark on a confirmed full write. addDocuments() now\n // returns a written count and tolerates per-batch failures, so a transient\n // Chroma error must NOT mark this observation as synced — otherwise the\n // backfill pass on next boot will skip past it (CodeRabbit review on PR\n // #2282).\n const written = await this.addDocuments(documents);\n if (written === documents.length) {\n ChromaSyncState.bump(project, 'observations', observationId);\n } else {\n logger.warn('CHROMA_SYNC', 'Observation watermark bump skipped — partial write', {\n observationId,\n project,\n requested: documents.length,\n written\n });\n }\n }\n\n async syncSummary(\n summaryId: number,\n memorySessionId: string,\n project: string,\n summary: ParsedSummary,\n promptNumber: number,\n createdAtEpoch: number,\n platformSource?: string\n ): Promise<void> {\n const stored: StoredSummary = {\n id: summaryId,\n memory_session_id: memorySessionId,\n project: project,\n merged_into_project: null,\n platform_source: platformSource ? normalizePlatformSource(platformSource) : normalizePlatformSource(undefined),\n request: summary.request,\n investigated: summary.investigated,\n learned: summary.learned,\n completed: summary.completed,\n next_steps: summary.next_steps,\n notes: summary.notes,\n prompt_number: promptNumber,\n created_at_epoch: createdAtEpoch\n };\n\n const documents = this.formatSummaryDocs(stored);\n\n logger.info('CHROMA_SYNC', 'Syncing summary', {\n summaryId,\n documentCount: documents.length,\n project\n });\n\n // Only bump on a confirmed full write — see syncObservation() for rationale.\n const written = await this.addDocuments(documents);\n if (written === documents.length) {\n ChromaSyncState.bump(project, 'summaries', summaryId);\n } else {\n logger.warn('CHROMA_SYNC', 'Summary watermark bump skipped — partial write', {\n summaryId,\n project,\n requested: documents.length,\n written\n });\n }\n }\n\n private formatUserPromptDoc(prompt: StoredUserPrompt): ChromaDocument {\n return {\n id: `prompt_${prompt.id}`,\n document: prompt.prompt_text,\n metadata: {\n sqlite_id: prompt.id,\n doc_type: 'user_prompt',\n memory_session_id: prompt.memory_session_id,\n project: prompt.project,\n platform_source: prompt.platform_source,\n created_at_epoch: prompt.created_at_epoch,\n prompt_number: prompt.prompt_number\n }\n };\n }\n\n async syncUserPrompt(\n promptId: number,\n memorySessionId: string,\n project: string,\n promptText: string,\n promptNumber: number,\n createdAtEpoch: number,\n platformSource?: string\n ): Promise<void> {\n const stored: StoredUserPrompt = {\n id: promptId,\n content_session_id: '', // Not needed for Chroma sync\n prompt_number: promptNumber,\n prompt_text: promptText,\n created_at_epoch: createdAtEpoch,\n memory_session_id: memorySessionId,\n project: project,\n platform_source: normalizePlatformSource(platformSource)\n };\n\n const document = this.formatUserPromptDoc(stored);\n\n logger.info('CHROMA_SYNC', 'Syncing user prompt', {\n promptId,\n project\n });\n\n // Only bump on a confirmed full write — see syncObservation() for rationale.\n const written = await this.addDocuments([document]);\n if (written === 1) {\n ChromaSyncState.bump(project, 'prompts', promptId);\n } else {\n logger.warn('CHROMA_SYNC', 'Prompt watermark bump skipped — write failed', {\n promptId,\n project,\n written\n });\n }\n }\n\n private async getExistingChromaIds(projectOverride?: string): Promise<{\n observations: Set<number>;\n summaries: Set<number>;\n prompts: Set<number>;\n }> {\n const targetProject = projectOverride ?? this.project;\n await this.ensureCollectionExists();\n\n const chromaMcp = ChromaMcpManager.getInstance();\n\n const observationIds = new Set<number>();\n const summaryIds = new Set<number>();\n const promptIds = new Set<number>();\n\n let offset = 0;\n const limit = 1000; \n\n logger.info('CHROMA_SYNC', 'Fetching existing Chroma document IDs...', { project: targetProject });\n\n while (true) {\n const result = await chromaMcp.callTool('chroma_get_documents', {\n collection_name: this.collectionName,\n limit: limit,\n offset: offset,\n where: { project: targetProject },\n include: ['metadatas']\n }) as any;\n\n const metadatas = result?.metadatas || [];\n\n if (metadatas.length === 0) {\n break; \n }\n\n for (const meta of metadatas) {\n if (meta && meta.sqlite_id) {\n const sqliteId = meta.sqlite_id as number;\n if (meta.doc_type === 'observation') {\n observationIds.add(sqliteId);\n } else if (meta.doc_type === 'session_summary') {\n summaryIds.add(sqliteId);\n } else if (meta.doc_type === 'user_prompt') {\n promptIds.add(sqliteId);\n }\n }\n }\n\n offset += limit;\n\n logger.debug('CHROMA_SYNC', 'Fetched batch of existing IDs', {\n project: targetProject,\n offset,\n batchSize: metadatas.length\n });\n }\n\n logger.info('CHROMA_SYNC', 'Existing IDs fetched', {\n project: targetProject,\n observations: observationIds.size,\n summaries: summaryIds.size,\n prompts: promptIds.size,\n total: observationIds.size + summaryIds.size + promptIds.size\n });\n\n return { observations: observationIds, summaries: summaryIds, prompts: promptIds };\n }\n\n async bootstrapWatermarksFromChroma(project: string): Promise<void> {\n const existing = await this.getExistingChromaIds(project);\n const max = (set: Set<number>): number => (set.size ? Math.max(...set) : 0);\n ChromaSyncState.replace(project, {\n observations: max(existing.observations),\n summaries: max(existing.summaries),\n prompts: max(existing.prompts)\n });\n logger.info('CHROMA_SYNC', 'Bootstrapped watermarks from Chroma', {\n project,\n watermarks: ChromaSyncState.get(project)\n });\n }\n\n async ensureBackfilled(projectOverride?: string, storeOverride?: SessionStore): Promise<void> {\n const backfillProject = projectOverride ?? this.project;\n logger.info('CHROMA_SYNC', 'Starting smart backfill', { project: backfillProject });\n\n await this.ensureCollectionExists();\n\n const watermarks = ChromaSyncState.get(backfillProject);\n\n const SessionStoreCtor = loadSessionStoreCtor();\n const db = storeOverride ?? new SessionStoreCtor();\n\n try {\n await this.runBackfillPipeline(db, backfillProject, watermarks);\n } catch (error) {\n logger.error('CHROMA_SYNC', 'Backfill failed', { project: backfillProject }, error instanceof Error ? error : new Error(String(error)));\n throw new Error(`Backfill failed: ${error instanceof Error ? error.message : String(error)}`);\n } finally {\n if (!storeOverride) {\n db.close();\n }\n }\n }\n\n private async runBackfillPipeline(\n db: SessionStore,\n backfillProject: string,\n watermarks: ProjectWatermarks\n ): Promise<void> {\n const allDocs = await this.backfillObservations(db, backfillProject, watermarks.observations);\n const summaryDocs = await this.backfillSummaries(db, backfillProject, watermarks.summaries);\n const promptDocs = await this.backfillPrompts(db, backfillProject, watermarks.prompts);\n\n logger.info('CHROMA_SYNC', 'Smart backfill complete', {\n project: backfillProject,\n synced: {\n observationDocs: allDocs.length,\n summaryDocs: summaryDocs.length,\n promptDocs: promptDocs.length\n },\n watermarks: ChromaSyncState.get(backfillProject)\n });\n }\n\n private async backfillObservations(\n db: SessionStore,\n backfillProject: string,\n watermark: number\n ): Promise<ChromaDocument[]> {\n const observations = db.db.prepare(`\n SELECT\n o.*,\n COALESCE(NULLIF(s.platform_source, ''), 'claude') as platform_source\n FROM observations o\n LEFT JOIN sdk_sessions s ON s.memory_session_id = o.memory_session_id\n WHERE o.project = ? AND o.id > ?\n ORDER BY o.id ASC\n `).all(backfillProject, watermark) as StoredObservation[];\n\n if (observations.length === 0) {\n return [];\n }\n\n const totalObsCount = db.db.prepare(`\n SELECT COUNT(*) as count FROM observations WHERE project = ?\n `).get(backfillProject) as { count: number };\n\n logger.info('CHROMA_SYNC', 'Backfilling observations', {\n project: backfillProject,\n missing: observations.length,\n watermark,\n total: totalObsCount.count\n });\n\n const allDocs: ChromaDocument[] = [];\n const obsByDocCount: Array<{ obs: StoredObservation; docs: ChromaDocument[] }> = [];\n for (const obs of observations) {\n const docs = this.formatObservationDocs(obs);\n allDocs.push(...docs);\n obsByDocCount.push({ obs, docs });\n }\n\n // Watermark must be durable per-batch: SIGKILL / OOM / reboot mid-flight\n // skips any trailing finally, so a once-at-end bump leaves the watermark\n // at zero and the next boot re-embeds everything (#2214, amplifies #2220).\n //\n // Non-contiguous failure guard: once any batch under-writes, ALL later\n // batches must also skip the watermark bump. The watermark is a single\n // monotonic id, so it cannot represent \"synced through 200, then a gap at\n // 201–250, then 251 onward\" — bumping past the gap would silently drop\n // 201–250 forever (CodeRabbit review on PR #2282).\n let writtenDocs = 0;\n let lastSyncedObsIdx = -1;\n let hadGap = false;\n for (let i = 0; i < allDocs.length; i += this.BATCH_SIZE) {\n const batch = allDocs.slice(i, i + this.BATCH_SIZE);\n const writtenInBatch = await this.addDocuments(batch);\n // Only advance the watermark for documents that actually landed in\n // Chroma. addDocuments() logs and continues on per-batch failures, so a\n // partial write must not mark unwritten docs as synced.\n if (writtenInBatch < batch.length) {\n hadGap = true;\n logger.debug('CHROMA_SYNC', 'Skipping watermark bump for failed/partial batch', {\n project: backfillProject,\n batchStart: i,\n requested: batch.length,\n written: writtenInBatch\n });\n continue;\n }\n if (hadGap) {\n // A previous batch left a gap; downstream batches cannot bump the\n // watermark even if they themselves succeeded.\n logger.debug('CHROMA_SYNC', 'Skipping watermark bump after prior gap', {\n project: backfillProject,\n batchStart: i\n });\n continue;\n }\n writtenDocs += writtenInBatch;\n\n let cursor = 0;\n for (let j = 0; j < obsByDocCount.length; j++) {\n cursor += obsByDocCount[j].docs.length;\n if (cursor <= writtenDocs) {\n lastSyncedObsIdx = j;\n } else {\n break;\n }\n }\n\n if (lastSyncedObsIdx >= 0) {\n ChromaSyncState.bump(\n backfillProject,\n 'observations',\n obsByDocCount[lastSyncedObsIdx].obs.id\n );\n }\n\n logger.debug('CHROMA_SYNC', 'Backfill progress', {\n project: backfillProject,\n progress: `${Math.min(i + this.BATCH_SIZE, allDocs.length)}/${allDocs.length}`\n });\n }\n\n return allDocs;\n }\n\n private async backfillSummaries(\n db: SessionStore,\n backfillProject: string,\n watermark: number\n ): Promise<ChromaDocument[]> {\n const summaries = db.db.prepare(`\n SELECT\n ss.*,\n COALESCE(NULLIF(s.platform_source, ''), 'claude') as platform_source\n FROM session_summaries ss\n LEFT JOIN sdk_sessions s ON s.memory_session_id = ss.memory_session_id\n WHERE ss.project = ? AND ss.id > ?\n ORDER BY ss.id ASC\n `).all(backfillProject, watermark) as StoredSummary[];\n\n if (summaries.length === 0) {\n return [];\n }\n\n const totalSummaryCount = db.db.prepare(`\n SELECT COUNT(*) as count FROM session_summaries WHERE project = ?\n `).get(backfillProject) as { count: number };\n\n logger.info('CHROMA_SYNC', 'Backfilling summaries', {\n project: backfillProject,\n missing: summaries.length,\n watermark,\n total: totalSummaryCount.count\n });\n\n const summaryDocs: ChromaDocument[] = [];\n const summaryByDocCount: Array<{ summary: StoredSummary; docs: ChromaDocument[] }> = [];\n for (const summary of summaries) {\n const docs = this.formatSummaryDocs(summary);\n summaryDocs.push(...docs);\n summaryByDocCount.push({ summary, docs });\n }\n\n // Non-contiguous failure guard: see backfillObservations() for rationale.\n let writtenDocs = 0;\n let lastSyncedIdx = -1;\n let hadGap = false;\n for (let i = 0; i < summaryDocs.length; i += this.BATCH_SIZE) {\n const batch = summaryDocs.slice(i, i + this.BATCH_SIZE);\n const writtenInBatch = await this.addDocuments(batch);\n // Only advance the watermark for documents that actually landed in\n // Chroma. See the analogous comment in backfillObservations().\n if (writtenInBatch < batch.length) {\n hadGap = true;\n logger.debug('CHROMA_SYNC', 'Skipping watermark bump for failed/partial batch', {\n project: backfillProject,\n batchStart: i,\n requested: batch.length,\n written: writtenInBatch\n });\n continue;\n }\n if (hadGap) {\n logger.debug('CHROMA_SYNC', 'Skipping watermark bump after prior gap', {\n project: backfillProject,\n batchStart: i\n });\n continue;\n }\n writtenDocs += writtenInBatch;\n\n let cursor = 0;\n for (let j = 0; j < summaryByDocCount.length; j++) {\n cursor += summaryByDocCount[j].docs.length;\n if (cursor <= writtenDocs) lastSyncedIdx = j;\n else break;\n }\n\n if (lastSyncedIdx >= 0) {\n ChromaSyncState.bump(\n backfillProject,\n 'summaries',\n summaryByDocCount[lastSyncedIdx].summary.id\n );\n }\n\n logger.debug('CHROMA_SYNC', 'Backfill progress', {\n project: backfillProject,\n progress: `${Math.min(i + this.BATCH_SIZE, summaryDocs.length)}/${summaryDocs.length}`\n });\n }\n\n return summaryDocs;\n }\n\n private async backfillPrompts(\n db: SessionStore,\n backfillProject: string,\n watermark: number\n ): Promise<ChromaDocument[]> {\n const prompts = db.db.prepare(`\n SELECT\n up.*,\n s.project,\n s.memory_session_id,\n COALESCE(NULLIF(s.platform_source, ''), 'claude') as platform_source\n FROM user_prompts up\n JOIN sdk_sessions s ON up.session_db_id = s.id\n WHERE s.project = ? AND up.id > ?\n ORDER BY up.id ASC\n `).all(backfillProject, watermark) as StoredUserPrompt[];\n\n if (prompts.length === 0) {\n return [];\n }\n\n const totalPromptCount = db.db.prepare(`\n SELECT COUNT(*) as count\n FROM user_prompts up\n JOIN sdk_sessions s ON up.session_db_id = s.id\n WHERE s.project = ?\n `).get(backfillProject) as { count: number };\n\n logger.info('CHROMA_SYNC', 'Backfilling user prompts', {\n project: backfillProject,\n missing: prompts.length,\n watermark,\n total: totalPromptCount.count\n });\n\n const promptDocs: ChromaDocument[] = [];\n for (const prompt of prompts) {\n promptDocs.push(this.formatUserPromptDoc(prompt));\n }\n\n // Prompts are 1 doc each — bump the watermark per batch so an interrupted\n // backfill resumes where it left off instead of re-embedding from zero.\n // Only advance the watermark when the batch actually wrote — partial\n // writes must not skip the failed prompts on restart.\n //\n // Non-contiguous failure guard: see backfillObservations() for rationale.\n let hadGap = false;\n for (let i = 0; i < promptDocs.length; i += this.BATCH_SIZE) {\n const batch = promptDocs.slice(i, i + this.BATCH_SIZE);\n const writtenInBatch = await this.addDocuments(batch);\n const upTo = Math.min(i + this.BATCH_SIZE, prompts.length);\n if (writtenInBatch < batch.length) {\n hadGap = true;\n logger.debug('CHROMA_SYNC', 'Skipping prompt watermark bump for failed/partial batch', {\n project: backfillProject,\n batchStart: i,\n requested: batch.length,\n written: writtenInBatch\n });\n continue;\n }\n if (hadGap) {\n logger.debug('CHROMA_SYNC', 'Skipping prompt watermark bump after prior gap', {\n project: backfillProject,\n batchStart: i\n });\n continue;\n }\n const lastSyncedPromptId = prompts[upTo - 1].id;\n ChromaSyncState.bump(backfillProject, 'prompts', lastSyncedPromptId);\n\n logger.debug('CHROMA_SYNC', 'Backfill progress', {\n project: backfillProject,\n progress: `${upTo}/${promptDocs.length}`\n });\n }\n\n return promptDocs;\n }\n\n async queryChroma(\n query: string,\n limit: number,\n whereFilter?: Record<string, any>\n ): Promise<{ ids: number[]; distances: number[]; metadatas: any[] }> {\n await this.ensureCollectionExists();\n\n let results: any;\n try {\n const chromaMcp = ChromaMcpManager.getInstance();\n results = await chromaMcp.callTool('chroma_query_documents', {\n collection_name: this.collectionName,\n query_texts: [query],\n n_results: limit,\n ...(whereFilter && { where: whereFilter }),\n include: ['documents', 'metadatas', 'distances']\n });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n const isConnectionError =\n errorMessage.includes('ECONNREFUSED') || \n errorMessage.includes('ENOTFOUND') || \n errorMessage.includes('fetch failed') || \n errorMessage.includes('subprocess closed') || \n errorMessage.includes('timed out'); \n\n if (isConnectionError) {\n this.collectionCreated = false;\n logger.error('CHROMA_SYNC', 'Connection lost during query',\n { project: this.project, query }, error as Error);\n throw new Error(`Chroma query failed - connection lost: ${errorMessage}`);\n }\n\n logger.error('CHROMA_SYNC', 'Query failed', { project: this.project, query }, error as Error);\n throw error;\n }\n\n return this.deduplicateQueryResults(results);\n }\n\n private deduplicateQueryResults(results: any): { ids: number[]; distances: number[]; metadatas: any[] } {\n const ids: number[] = [];\n const seen = new Set<string>();\n const docIds = results?.ids?.[0] || [];\n const rawMetadatas = results?.metadatas?.[0] || [];\n const rawDistances = results?.distances?.[0] || [];\n\n const metadatas: any[] = [];\n const distances: number[] = [];\n\n for (let i = 0; i < docIds.length; i++) {\n const docId = docIds[i];\n const obsMatch = docId.match(/obs_(\\d+)_/);\n const summaryMatch = docId.match(/summary_(\\d+)_/);\n const promptMatch = docId.match(/prompt_(\\d+)/);\n\n let sqliteId: number | null = null;\n let entityType: string | null = null;\n if (obsMatch) {\n sqliteId = parseInt(obsMatch[1], 10);\n entityType = 'observation';\n } else if (summaryMatch) {\n sqliteId = parseInt(summaryMatch[1], 10);\n entityType = 'session_summary';\n } else if (promptMatch) {\n sqliteId = parseInt(promptMatch[1], 10);\n entityType = 'user_prompt';\n }\n\n if (sqliteId !== null && entityType) {\n const dedupeKey = `${entityType}:${sqliteId}`;\n if (seen.has(dedupeKey)) continue;\n seen.add(dedupeKey);\n ids.push(sqliteId);\n metadatas.push(rawMetadatas[i] ?? null);\n distances.push(rawDistances[i] ?? 0);\n }\n }\n\n return { ids, distances, metadatas };\n }\n\n /** Maximum number of concurrent project backfills to run at once. */\n private static readonly BACKFILL_CONCURRENCY_LIMIT = 3;\n\n /** Guard flag to prevent overlapping backfill runs from fire-and-forget callers. */\n private static backfillInProgress = false;\n\n /**\n * Backfill all projects that have observations in SQLite but may be missing from Chroma.\n * Uses a single shared ChromaSync('claude-mem') instance and Chroma connection.\n * Per-project scoping is passed as a parameter to ensureBackfilled(), avoiding\n * instance state mutation. All documents land in the cm__claude-mem collection\n * with project scoped via metadata, matching how DatabaseManager and SearchManager operate.\n * Designed to be called fire-and-forget on worker startup.\n *\n * Concurrency: processes at most BACKFILL_CONCURRENCY_LIMIT projects in parallel\n * to bound CPU and memory pressure from concurrent Chroma embedding operations.\n * A re-entrant guard prevents overlapping backfill runs from accumulating.\n */\n static async backfillAllProjects(storeOverride?: SessionStore): Promise<void> {\n if (ChromaSync.backfillInProgress) {\n logger.info('CHROMA_SYNC', 'Backfill already in progress, skipping duplicate run');\n return;\n }\n\n // Allocate first so a constructor throw cannot leave the guard stuck true\n // and silently skip every subsequent backfill (CodeRabbit review on PR\n // #2282). The guard only flips to true after both resources are alive,\n // and the finally always clears it.\n let db: SessionStore | undefined;\n let sync: ChromaSync | undefined;\n try {\n const SessionStoreCtor = loadSessionStoreCtor();\n db = storeOverride ?? new SessionStoreCtor();\n sync = new ChromaSync('claude-mem');\n } catch (error) {\n logger.error('CHROMA_SYNC', 'Failed to initialize backfill resources',\n {}, error instanceof Error ? error : new Error(String(error)));\n // Best-effort cleanup if SessionStore allocated but ChromaSync threw.\n if (db && !storeOverride) {\n try { db.close(); } catch { /* ignore */ }\n }\n throw error;\n }\n\n ChromaSync.backfillInProgress = true;\n try {\n const projects = db.db.prepare(\n 'SELECT DISTINCT project FROM observations WHERE project IS NOT NULL AND project != ?'\n ).all('') as { project: string }[];\n\n logger.info('CHROMA_SYNC', `Backfill check for ${projects.length} projects`);\n\n if (!ChromaSyncState.exists()) {\n logger.info('CHROMA_SYNC', 'Watermark cache missing — bootstrapping from Chroma (one-time)');\n for (const { project } of projects) {\n try {\n await sync.bootstrapWatermarksFromChroma(project);\n } catch (error) {\n logger.error('CHROMA_SYNC', `Bootstrap failed for project: ${project}`,\n {}, error instanceof Error ? error : new Error(String(error)));\n }\n }\n logger.info('CHROMA_SYNC', 'Bootstrap complete — incremental backfills will use watermarks');\n }\n\n // Process projects in chunks of BACKFILL_CONCURRENCY_LIMIT to bound\n // CPU/memory pressure from concurrent Chroma embedding operations.\n // Each chunk runs its projects in parallel; we wait for the entire chunk\n // before starting the next one. Simple and predictable — no semaphore\n // overhead, no unbounded fan-out.\n const concurrency = ChromaSync.BACKFILL_CONCURRENCY_LIMIT;\n for (let i = 0; i < projects.length; i += concurrency) {\n const chunk = projects.slice(i, i + concurrency);\n const chunkResults = await Promise.allSettled(\n chunk.map(({ project }) => sync!.ensureBackfilled(project, db!))\n );\n\n for (let j = 0; j < chunkResults.length; j++) {\n const result = chunkResults[j];\n if (result.status === 'rejected') {\n const project = chunk[j].project;\n const error = result.reason;\n if (error instanceof Error) {\n logger.error('CHROMA_SYNC', `Backfill failed for project: ${project}`, {}, error);\n } else {\n logger.error('CHROMA_SYNC', `Backfill failed for project: ${project}`, { error: String(error) });\n }\n // Continue to next chunk — don't let one failure stop others\n }\n }\n }\n } finally {\n ChromaSync.backfillInProgress = false;\n if (sync) {\n try { await sync.close(); } catch (closeError) {\n logger.debug('CHROMA_SYNC', 'sync.close() failed during backfill teardown',\n {}, closeError instanceof Error ? closeError : new Error(String(closeError)));\n }\n }\n if (!storeOverride && db) {\n try { db.close(); } catch (closeError) {\n logger.debug('CHROMA_SYNC', 'db.close() failed during backfill teardown',\n {}, closeError instanceof Error ? closeError : new Error(String(closeError)));\n }\n }\n }\n }\n\n async updateMergedIntoProject(\n sqliteIds: number[],\n mergedIntoProject: string\n ): Promise<void> {\n if (sqliteIds.length === 0) return;\n\n await this.ensureCollectionExists();\n const chromaMcp = ChromaMcpManager.getInstance();\n\n let totalPatched = 0;\n\n for (let i = 0; i < sqliteIds.length; i += this.BATCH_SIZE) {\n const idBatch = sqliteIds.slice(i, i + this.BATCH_SIZE);\n\n const existing = await chromaMcp.callTool('chroma_get_documents', {\n collection_name: this.collectionName,\n where: { sqlite_id: { $in: idBatch } },\n include: ['metadatas']\n }) as { ids?: string[]; metadatas?: Array<Record<string, any> | null> };\n\n const docIds: string[] = existing?.ids ?? [];\n if (docIds.length === 0) continue;\n\n const metadatas = (existing?.metadatas ?? []).map(m => {\n const merged: Record<string, any> = {\n ...(m ?? {}),\n merged_into_project: mergedIntoProject\n };\n return Object.fromEntries(\n Object.entries(merged).filter(\n ([, v]) => v !== null && v !== undefined && v !== ''\n )\n );\n });\n\n await chromaMcp.callTool('chroma_update_documents', {\n collection_name: this.collectionName,\n ids: docIds,\n metadatas\n });\n totalPatched += docIds.length;\n }\n\n logger.info('CHROMA_SYNC', 'merged_into_project metadata patched', {\n collection: this.collectionName,\n mergedIntoProject,\n sqliteIdCount: sqliteIds.length,\n chromaDocsPatched: totalPatched\n });\n }\n\n async close(): Promise<void> {\n logger.info('CHROMA_SYNC', 'ChromaSync closed', { project: this.project });\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { createHash } from 'crypto';\nimport { SERVER_JOB_KIND_PREFIX, type ServerGenerationJobKind } from './types.js';\n\nexport interface ServerJobIdParts {\n kind: ServerGenerationJobKind;\n team_id: string;\n project_id: string;\n source_type: string;\n source_id: string;\n}\n\n// SHA-256-derived deterministic IDs avoid Redis key collisions across tenants\n// and keep BullMQ jobId deduplication intact across process restarts.\n// Format: `${kindPrefix}_${sha256hex}` with NO ':' characters (BullMQ uses ':'\n// internally as a key separator; embedding ':' in jobIds causes scan/state\n// confusion).\nexport function buildServerJobId(parts: ServerJobIdParts): string {\n const prefix = SERVER_JOB_KIND_PREFIX[parts.kind];\n const canonical = JSON.stringify({\n kind: parts.kind,\n team_id: parts.team_id,\n project_id: parts.project_id,\n source_type: parts.source_type,\n source_id: parts.source_id\n });\n const digest = createHash('sha256').update(canonical).digest('hex');\n return `${prefix}_${digest}`;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { z } from 'zod';\nimport type {\n ObservationGenerationJobSourceType,\n ObservationGenerationJobStatus\n} from '../../storage/postgres/generation-jobs.js';\n\nexport type ServerGenerationJobKind = 'event' | 'event-batch' | 'summary' | 'reindex';\n\nexport type ServerGenerationJobStatus = ObservationGenerationJobStatus;\n\n// Phase 11 — every BullMQ job carries the full team-aware tracing surface so\n// the worker can audit and scope-check on every retry. team_id and project_id\n// are advisory: the worker MUST reload the canonical outbox row from Postgres\n// and compare these fields before any side effect. Treating these as auth\n// authority would be a bypass — the comparison is a tampering detector, not\n// the auth gate.\nexport interface ServerGenerationJob {\n kind: ServerGenerationJobKind;\n team_id: string;\n project_id: string;\n source_type: ObservationGenerationJobSourceType;\n source_id: string;\n generation_job_id: string;\n // Identity of the API key that initiated this job at the HTTP boundary.\n // Reused at execution time to detect revocation between enqueue and run.\n api_key_id: string | null;\n // The actor associated with the api key at enqueue time. Audit-only;\n // never trust this for authz decisions.\n actor_id: string | null;\n // Legacy adapter or surface that produced the source row, for routing\n // and audit (e.g. 'api', 'hooks', 'mcp', 'compat:sessions-observations').\n source_adapter: string;\n // Phase 12 — request correlation id, optional but always serialized as a\n // nullable field so downstream consumers can rely on shape stability.\n request_id?: string | null;\n}\n\nexport interface GenerateObservationsForEventJob extends ServerGenerationJob {\n kind: 'event';\n agent_event_id: string;\n}\n\nexport interface GenerateObservationsForEventBatchJob extends ServerGenerationJob {\n kind: 'event-batch';\n agent_event_ids: string[];\n}\n\nexport interface GenerateSessionSummaryJob extends ServerGenerationJob {\n kind: 'summary';\n server_session_id: string;\n}\n\nexport interface ReindexObservationJob extends ServerGenerationJob {\n kind: 'reindex';\n observation_id: string;\n}\n\nexport type ServerGenerationJobPayload =\n | GenerateObservationsForEventJob\n | GenerateObservationsForEventBatchJob\n | GenerateSessionSummaryJob\n | ReindexObservationJob;\n\nexport const SERVER_JOB_QUEUE_NAMES: Record<ServerGenerationJobKind, string> = {\n event: 'server_beta_generate_event',\n 'event-batch': 'server_beta_generate_event_batch',\n summary: 'server_beta_generate_summary',\n reindex: 'server_beta_reindex'\n};\n\nexport const SERVER_JOB_KIND_PREFIX: Record<ServerGenerationJobKind, string> = {\n event: 'evt',\n 'event-batch': 'evtb',\n summary: 'sum',\n reindex: 'rdx'\n};\n\n// Phase 11 — Zod schema validates payloads at the queue boundary so a\n// malformed enqueue is rejected synchronously rather than silently producing\n// a job the worker can't audit. Required fields here mirror the\n// ServerGenerationJob interface; a missing team_id, project_id, or\n// generation_job_id should always be a programmer error caught at enqueue.\n\nconst baseFieldsSchema = z.object({\n team_id: z.string().min(1, 'team_id is required'),\n project_id: z.string().min(1, 'project_id is required'),\n source_type: z.enum(['agent_event', 'session_summary', 'observation_reindex']),\n source_id: z.string().min(1, 'source_id is required'),\n generation_job_id: z.string().min(1, 'generation_job_id is required'),\n // api_key_id and actor_id are nullable to accommodate local-dev/system\n // enqueues, but the *field* must be present in the payload so audit\n // records always render the same shape.\n api_key_id: z.string().min(1).nullable(),\n actor_id: z.string().min(1).nullable(),\n source_adapter: z.string().min(1, 'source_adapter is required'),\n // Phase 12 — request_id is optional in the schema (older jobs predating\n // this phase have nullable/missing values) but always passes through to\n // logs and audit when present.\n request_id: z.string().min(1).nullable().optional(),\n});\n\nexport const GenerateObservationsForEventJobSchema = baseFieldsSchema.extend({\n kind: z.literal('event'),\n agent_event_id: z.string().min(1),\n});\n\nexport const GenerateObservationsForEventBatchJobSchema = baseFieldsSchema.extend({\n kind: z.literal('event-batch'),\n agent_event_ids: z.array(z.string().min(1)).min(1),\n});\n\nexport const GenerateSessionSummaryJobSchema = baseFieldsSchema.extend({\n kind: z.literal('summary'),\n server_session_id: z.string().min(1),\n});\n\nexport const ReindexObservationJobSchema = baseFieldsSchema.extend({\n kind: z.literal('reindex'),\n observation_id: z.string().min(1),\n});\n\nexport const ServerGenerationJobPayloadSchema = z.discriminatedUnion('kind', [\n GenerateObservationsForEventJobSchema,\n GenerateObservationsForEventBatchJobSchema,\n GenerateSessionSummaryJobSchema,\n ReindexObservationJobSchema,\n]);\n\nexport class ServerGenerationJobPayloadValidationError extends Error {\n readonly issues: z.ZodIssue[];\n\n constructor(issues: z.ZodIssue[]) {\n super(`invalid server generation job payload: ${issues.map(i => i.message).join('; ')}`);\n this.issues = issues;\n }\n}\n\n/**\n * Validate a candidate BullMQ payload against the discriminated union and\n * return a typed payload, or throw `ServerGenerationJobPayloadValidationError`.\n * Use this at every enqueue site so a malformed payload never enters the\n * transport — the worker MUST also re-validate from Postgres but defense in\n * depth is cheap.\n */\nexport function assertServerGenerationJobPayload(\n candidate: unknown,\n): ServerGenerationJobPayload {\n const result = ServerGenerationJobPayloadSchema.safeParse(candidate);\n if (!result.success) {\n throw new ServerGenerationJobPayloadValidationError(result.error.issues);\n }\n return result.data as ServerGenerationJobPayload;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport type { JobsOptions } from 'bullmq';\nimport type {\n GenerateObservationsForEventJob,\n GenerateSessionSummaryJob,\n} from '../jobs/types.js';\nimport { buildServerJobId } from '../jobs/job-id.js';\nimport type { PostgresAgentEvent } from '../../storage/postgres/agent-events.js';\nimport type { PostgresObservationGenerationJob } from '../../storage/postgres/generation-jobs.js';\n\n// SessionGenerationPolicy decides WHEN to enqueue work for the BullMQ event\n// and summary lanes. It is configurable via:\n// - CLAUDE_MEM_SERVER_SESSION_POLICY env var (per-process default)\n// - per-call override (per-team settings can plug in here later)\n//\n// Three policies are supported:\n// - 'per-event' (default): enqueue immediately on every event POST.\n// Matches Phase 4/5 behavior.\n// - 'debounce': enqueue with `delay`; when a new event arrives within\n// the window, replace the delayed job (deterministic\n// BullMQ jobId means re-add(jobId, ...) overwrites the\n// waiting entry, and removeOnComplete/Fail keep things\n// tidy). Outbox row is canonical so durability is safe.\n// - 'end-of-session': only enqueue summary jobs at /v1/sessions/:id/end.\n// Per-event posts skip BullMQ entirely; the outbox row\n// remains in `queued` state and startup reconciliation\n// will publish it later (or it can be cancelled).\n//\n// Anti-pattern guard: the policy MUST NOT use ActiveSession-style cached\n// state. Inputs are always reloaded by the caller from Postgres before this\n// fires.\n\nexport type ServerSessionGenerationPolicy = 'per-event' | 'debounce' | 'end-of-session';\n\nconst DEFAULT_DEBOUNCE_MS = 5000;\n\nexport interface SessionGenerationPolicyOptions {\n policy?: ServerSessionGenerationPolicy;\n debounceWindowMs?: number;\n}\n\nexport function resolveSessionGenerationPolicy(\n options: SessionGenerationPolicyOptions = {},\n): { policy: ServerSessionGenerationPolicy; debounceWindowMs: number } {\n const envPolicy = (process.env.CLAUDE_MEM_SERVER_SESSION_POLICY ?? '').trim().toLowerCase();\n const policy: ServerSessionGenerationPolicy = options.policy\n ?? (envPolicy === 'debounce' || envPolicy === 'end-of-session' || envPolicy === 'per-event'\n ? envPolicy\n : 'per-event');\n const debounceWindowMs = options.debounceWindowMs\n ?? (Number.parseInt(process.env.CLAUDE_MEM_SERVER_SESSION_DEBOUNCE_MS ?? '', 10)\n || DEFAULT_DEBOUNCE_MS);\n return {\n policy,\n debounceWindowMs: Number.isFinite(debounceWindowMs) && debounceWindowMs > 0\n ? debounceWindowMs\n : DEFAULT_DEBOUNCE_MS,\n };\n}\n\nexport interface EnqueueEventDecisionInput {\n event: PostgresAgentEvent;\n outbox: PostgresObservationGenerationJob;\n // Phase 11 — identity context captured at HTTP ingest time so the BullMQ\n // payload carries every audit field. apiKeyId may be null for local-dev\n // enqueues and `actorId` follows the api key's `actor_id` column.\n apiKeyId?: string | null;\n actorId?: string | null;\n sourceAdapter?: string | null;\n // Phase 12 — request correlation id minted at the HTTP boundary.\n requestId?: string | null;\n}\n\nexport interface EnqueueEventDecision {\n shouldEnqueue: boolean;\n jobId: string;\n payload: GenerateObservationsForEventJob;\n jobsOptions?: JobsOptions;\n reason: 'per-event' | 'debounce' | 'end-of-session-skip';\n}\n\nexport function buildEnqueueEventDecision(\n input: EnqueueEventDecisionInput,\n options: SessionGenerationPolicyOptions = {},\n): EnqueueEventDecision {\n const resolved = resolveSessionGenerationPolicy(options);\n const jobId = input.outbox.bullmqJobId ?? buildServerJobId({\n kind: 'event',\n team_id: input.event.teamId,\n project_id: input.event.projectId,\n source_type: 'agent_event',\n source_id: input.event.id,\n });\n const payload: GenerateObservationsForEventJob = {\n kind: 'event',\n team_id: input.outbox.teamId,\n project_id: input.outbox.projectId,\n source_type: 'agent_event',\n source_id: input.event.id,\n generation_job_id: input.outbox.id,\n agent_event_id: input.event.id,\n api_key_id: input.apiKeyId ?? null,\n actor_id: input.actorId ?? null,\n source_adapter: input.sourceAdapter ?? input.event.sourceAdapter ?? 'api',\n request_id: input.requestId ?? null,\n };\n\n if (resolved.policy === 'end-of-session') {\n return { shouldEnqueue: false, jobId, payload, reason: 'end-of-session-skip' };\n }\n\n if (resolved.policy === 'debounce') {\n return {\n shouldEnqueue: true,\n jobId,\n payload,\n jobsOptions: { delay: resolved.debounceWindowMs },\n reason: 'debounce',\n };\n }\n\n return { shouldEnqueue: true, jobId, payload, reason: 'per-event' };\n}\n\n// Minimal queue surface used by scheduleDebouncedEventJob. Declared as an\n// interface (instead of `Pick<ServerJobQueue<...>, ...>`) so the parameter\n// accepts ServerJobQueue<ServerGenerationJobPayload> at the call site without\n// triggering invariant TPayload type errors. The ServerJobQueue.add signature\n// is structurally compatible — it requires `payload: TPayload`, and we only\n// hand in narrowed payloads.\nexport interface DebounceableEventQueue {\n add(jobId: string, payload: GenerateObservationsForEventJob, options?: JobsOptions): Promise<void>;\n remove(jobId: string): Promise<void>;\n getJob(jobId: string): Promise<unknown>;\n}\n\n/**\n * Apply a debounce decision to a BullMQ queue. If a delayed job already exists\n * for this deterministic id, BullMQ's `add(jobId, ...)` will be a no-op, so we\n * proactively remove it first so the new event's delay window starts fresh.\n *\n * This implements the \"if a new event arrives within window, replace the\n * delayed job\" requirement.\n */\nexport async function scheduleDebouncedEventJob(\n queue: DebounceableEventQueue,\n decision: EnqueueEventDecision,\n): Promise<void> {\n if (!decision.shouldEnqueue) return;\n if (decision.reason === 'debounce') {\n try {\n const existing = await queue.getJob(decision.jobId);\n if (existing) {\n await queue.remove(decision.jobId);\n }\n } catch {\n // best-effort; if remove fails because the job already moved to active\n // we just let `add` no-op or fail through to the caller's error handler\n }\n }\n await queue.add(decision.jobId, decision.payload, decision.jobsOptions);\n}\n\nexport interface BuildSummaryJobInput {\n serverSessionId: string;\n teamId: string;\n projectId: string;\n generationJobId: string;\n // Phase 11 — same identity context the event-payload builder receives.\n apiKeyId?: string | null;\n actorId?: string | null;\n sourceAdapter?: string | null;\n // Phase 12 — request correlation id flows into the summary lane too.\n requestId?: string | null;\n}\n\nexport function buildSummaryJobId(input: {\n serverSessionId: string;\n teamId: string;\n projectId: string;\n}): string {\n return buildServerJobId({\n kind: 'summary',\n team_id: input.teamId,\n project_id: input.projectId,\n source_type: 'session_summary',\n source_id: input.serverSessionId,\n });\n}\n\nexport function buildSummaryJobPayload(input: BuildSummaryJobInput): GenerateSessionSummaryJob {\n return {\n kind: 'summary',\n team_id: input.teamId,\n project_id: input.projectId,\n source_type: 'session_summary',\n source_id: input.serverSessionId,\n generation_job_id: input.generationJobId,\n server_session_id: input.serverSessionId,\n api_key_id: input.apiKeyId ?? null,\n actor_id: input.actorId ?? null,\n source_adapter: input.sourceAdapter ?? 'api',\n request_id: input.requestId ?? null,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\n\n// Shared event-ingest path used by both `/v1/events` (canonical) and\n// `src/server/compat/SessionsObservationsAdapter.ts` (legacy translator).\n// Centralizes the transactional write (event row + outbox row + lifecycle\n// log) and the post-commit BullMQ enqueue so both call sites apply the\n// exact same SessionGenerationPolicy and outbox-then-publish guarantees.\n//\n// This module MUST NOT import from src/services/worker/* — the whole point\n// of Phase 9 is to give the compat adapters a translation surface that\n// reaches Server beta core directly, with no worker-layer detours.\n\nimport type { CreatePostgresAgentEventInput, PostgresAgentEvent } from '../../storage/postgres/agent-events.js';\nimport { PostgresAgentEventsRepository } from '../../storage/postgres/agent-events.js';\nimport {\n PostgresObservationGenerationJobEventsRepository,\n PostgresObservationGenerationJobRepository,\n type PostgresObservationGenerationJob,\n} from '../../storage/postgres/generation-jobs.js';\nimport type { PostgresPool } from '../../storage/postgres/pool.js';\nimport { withPostgresTransaction } from '../../storage/postgres/pool.js';\nimport { logger } from '../../utils/logger.js';\nimport { buildServerJobId } from '../jobs/job-id.js';\nimport type { GenerateObservationsForEventJob } from '../jobs/types.js';\nimport {\n buildEnqueueEventDecision,\n scheduleDebouncedEventJob,\n type ServerSessionGenerationPolicy,\n} from '../runtime/SessionGenerationPolicy.js';\nimport { newId } from '../../storage/postgres/utils.js';\n\nfunction buildEventBullmqPayload(input: {\n outboxId: string;\n event: PostgresAgentEvent;\n apiKeyId: string | null;\n actorId: string | null;\n sourceAdapter: string | null;\n requestId: string | null;\n}): GenerateObservationsForEventJob {\n return {\n kind: 'event',\n team_id: input.event.teamId,\n project_id: input.event.projectId,\n source_type: 'agent_event',\n source_id: input.event.id,\n generation_job_id: input.outboxId,\n agent_event_id: input.event.id,\n api_key_id: input.apiKeyId,\n actor_id: input.actorId,\n source_adapter: input.sourceAdapter ?? input.event.sourceAdapter ?? 'api',\n request_id: input.requestId,\n };\n}\n\nconst EVENT_JOB_TYPE = 'observation_generate_for_event';\n\nexport type EnqueueOutcome = 'enqueued' | 'queued_only' | 'skipped';\n\nexport interface IngestEventsServiceOptions {\n pool: PostgresPool;\n // Lazy queue resolver so the service does not depend on the queue manager\n // type and tests can swap in a fake. When this returns null, the outbox\n // row stays `queued` and Phase 3 startup reconciliation will publish it.\n resolveEventQueue: () => EventQueueLike | null;\n sessionPolicy?: ServerSessionGenerationPolicy;\n}\n\nexport interface EventQueueLike {\n add(jobId: string, payload: unknown, options?: unknown): Promise<unknown>;\n}\n\nexport interface IngestEventResult {\n event: PostgresAgentEvent;\n outbox: PostgresObservationGenerationJob | null;\n enqueueState: EnqueueOutcome;\n}\n\nexport interface IngestEventOptions {\n generate?: boolean;\n source?: string;\n // Phase 11 — identity context that flows from the HTTP auth boundary into\n // the BullMQ payload and audit log. None of these are auth gates: the\n // worker reloads and re-validates from Postgres before any side effect.\n apiKeyId?: string | null;\n actorId?: string | null;\n sourceAdapter?: string | null;\n // Phase 12 — opaque correlation id minted at the HTTP middleware so\n // generator logs and audit rows can pivot back to the originating request.\n requestId?: string | null;\n}\n\nexport class IngestEventsService {\n constructor(private readonly options: IngestEventsServiceOptions) {}\n\n async ingestOne(\n input: CreatePostgresAgentEventInput,\n opts: IngestEventOptions = {},\n ): Promise<IngestEventResult> {\n const generate = opts.generate ?? true;\n const source = opts.source ?? 'http_post_v1_events';\n\n const txResult = await withPostgresTransaction(this.options.pool, async (client) => {\n const eventsRepo = new PostgresAgentEventsRepository(client);\n const inserted = await eventsRepo.create(input);\n\n if (!generate) {\n return { event: inserted, outbox: null as PostgresObservationGenerationJob | null };\n }\n\n const jobsRepo = new PostgresObservationGenerationJobRepository(client);\n const eventsLogRepo = new PostgresObservationGenerationJobEventsRepository(client);\n // Pre-generate the outbox id so we can build the BullMQ payload (which\n // references generation_job_id) and persist it on the row. Reconciliation\n // and operator retry rely on this persisted payload to re-enqueue a\n // payload that passes assertServerGenerationJobPayload at the worker.\n const outboxId = newId();\n const bullmqPayload = buildEventBullmqPayload({\n outboxId,\n event: inserted,\n apiKeyId: opts.apiKeyId ?? null,\n actorId: opts.actorId ?? null,\n sourceAdapter: opts.sourceAdapter ?? null,\n requestId: opts.requestId ?? null,\n });\n const outbox = await jobsRepo.create({\n id: outboxId,\n projectId: inserted.projectId,\n teamId: inserted.teamId,\n sourceType: 'agent_event',\n sourceId: inserted.id,\n agentEventId: inserted.id,\n serverSessionId: inserted.serverSessionId,\n jobType: EVENT_JOB_TYPE,\n bullmqJobId: buildServerJobId({\n kind: 'event',\n team_id: inserted.teamId,\n project_id: inserted.projectId,\n source_type: 'agent_event',\n source_id: inserted.id,\n }),\n payload: bullmqPayload as unknown as Record<string, unknown>,\n });\n await eventsLogRepo.append({\n generationJobId: outbox.id,\n projectId: outbox.projectId,\n teamId: outbox.teamId,\n eventType: 'queued',\n statusAfter: outbox.status,\n attempt: outbox.attempts,\n details: { source },\n });\n return { event: inserted, outbox };\n });\n\n let enqueueState: EnqueueOutcome = 'skipped';\n if (txResult.outbox) {\n enqueueState = await this.publishEventJob(txResult.event, txResult.outbox, opts);\n }\n return { event: txResult.event, outbox: txResult.outbox, enqueueState };\n }\n\n async ingestBatch(\n inputs: CreatePostgresAgentEventInput[],\n opts: IngestEventOptions = {},\n ): Promise<IngestEventResult[]> {\n const generate = opts.generate ?? true;\n const source = opts.source ?? 'http_post_v1_events_batch';\n\n const txResults = await withPostgresTransaction(this.options.pool, async (client) => {\n const eventsRepo = new PostgresAgentEventsRepository(client);\n const jobsRepo = new PostgresObservationGenerationJobRepository(client);\n const eventsLogRepo = new PostgresObservationGenerationJobEventsRepository(client);\n const acc: { event: PostgresAgentEvent; outbox: PostgresObservationGenerationJob | null }[] = [];\n for (const input of inputs) {\n const event = await eventsRepo.create(input);\n if (!generate) {\n acc.push({ event, outbox: null });\n continue;\n }\n const outboxId = newId();\n const bullmqPayload = buildEventBullmqPayload({\n outboxId,\n event,\n apiKeyId: opts.apiKeyId ?? null,\n actorId: opts.actorId ?? null,\n sourceAdapter: opts.sourceAdapter ?? null,\n requestId: opts.requestId ?? null,\n });\n const outbox = await jobsRepo.create({\n id: outboxId,\n projectId: event.projectId,\n teamId: event.teamId,\n sourceType: 'agent_event',\n sourceId: event.id,\n agentEventId: event.id,\n serverSessionId: event.serverSessionId,\n jobType: EVENT_JOB_TYPE,\n bullmqJobId: buildServerJobId({\n kind: 'event',\n team_id: event.teamId,\n project_id: event.projectId,\n source_type: 'agent_event',\n source_id: event.id,\n }),\n payload: bullmqPayload as unknown as Record<string, unknown>,\n });\n await eventsLogRepo.append({\n generationJobId: outbox.id,\n projectId: outbox.projectId,\n teamId: outbox.teamId,\n eventType: 'queued',\n statusAfter: outbox.status,\n attempt: outbox.attempts,\n details: { source },\n });\n acc.push({ event, outbox });\n }\n return acc;\n });\n\n return Promise.all(txResults.map(async ({ event, outbox }) => {\n const enqueueState: EnqueueOutcome = outbox\n ? await this.publishEventJob(event, outbox, opts)\n : 'skipped';\n return { event, outbox, enqueueState };\n }));\n }\n\n private async publishEventJob(\n event: PostgresAgentEvent,\n outbox: PostgresObservationGenerationJob,\n opts: IngestEventOptions = {},\n ): Promise<'enqueued' | 'queued_only'> {\n const queue = this.options.resolveEventQueue();\n if (!queue) {\n return 'queued_only';\n }\n const policyOptions: { policy?: ServerSessionGenerationPolicy; debounceWindowMs?: number } = {};\n if (this.options.sessionPolicy !== undefined) {\n policyOptions.policy = this.options.sessionPolicy;\n }\n const decision = buildEnqueueEventDecision(\n {\n event,\n outbox,\n apiKeyId: opts.apiKeyId ?? null,\n actorId: opts.actorId ?? null,\n sourceAdapter: opts.sourceAdapter ?? event.sourceAdapter ?? null,\n // Phase 12 — flow request_id into the BullMQ payload so the worker\n // can emit it in [generation] logs and the audit row.\n requestId: opts.requestId ?? null,\n },\n policyOptions,\n );\n if (!decision.shouldEnqueue) {\n return 'queued_only';\n }\n try {\n await scheduleDebouncedEventJob(queue as never, decision);\n return 'enqueued';\n } catch (error) {\n logger.warn('SYSTEM', 'failed to publish event generation job to BullMQ', {\n outboxId: outbox.id,\n error: error instanceof Error ? error.message : String(error),\n });\n return 'queued_only';\n }\n }\n}\n","\nimport { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport type { ModeConfig, ObservationType } from './types.js';\nimport { logger } from '../../utils/logger.js';\nimport { getPackageRoot } from '../../shared/paths.js';\n\nexport class ModeManager {\n private static instance: ModeManager | null = null;\n private activeMode: ModeConfig | null = null;\n private modesDir: string;\n\n private constructor() {\n const packageRoot = getPackageRoot();\n \n const possiblePaths = [\n ...(process.env.CLAUDE_MEM_MODES_DIR ? [process.env.CLAUDE_MEM_MODES_DIR] : []),\n join(packageRoot, 'modes'), // Production (plugin/modes)\n join(packageRoot, '..', 'plugin', 'modes'), // Development (src/../plugin/modes)\n ];\n\n const foundPath = possiblePaths.find(p => existsSync(p));\n this.modesDir = foundPath || possiblePaths[0];\n }\n\n static getInstance(): ModeManager {\n if (!ModeManager.instance) {\n ModeManager.instance = new ModeManager();\n }\n return ModeManager.instance;\n }\n\n private parseInheritance(modeId: string): {\n hasParent: boolean;\n parentId: string;\n overrideId: string;\n } {\n const parts = modeId.split('--');\n\n if (parts.length === 1) {\n return { hasParent: false, parentId: '', overrideId: '' };\n }\n\n if (parts.length > 2) {\n throw new Error(\n `Invalid mode inheritance: ${modeId}. Only one level of inheritance supported (parent--override)`\n );\n }\n\n return {\n hasParent: true,\n parentId: parts[0],\n overrideId: modeId \n };\n }\n\n private isPlainObject(value: unknown): boolean {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value)\n );\n }\n\n private deepMerge<T>(base: T, override: Partial<T>): T {\n const result = { ...base } as T;\n\n for (const key in override) {\n const overrideValue = override[key];\n const baseValue = base[key];\n\n if (this.isPlainObject(overrideValue) && this.isPlainObject(baseValue)) {\n result[key] = this.deepMerge(baseValue, overrideValue as any);\n } else {\n result[key] = overrideValue as T[Extract<keyof T, string>];\n }\n }\n\n return result;\n }\n\n private loadModeFile(modeId: string): ModeConfig {\n const modePath = join(this.modesDir, `${modeId}.json`);\n\n if (!existsSync(modePath)) {\n throw new Error(`Mode file not found: ${modePath}`);\n }\n\n const jsonContent = readFileSync(modePath, 'utf-8');\n return JSON.parse(jsonContent) as ModeConfig;\n }\n\n loadMode(modeId: string): ModeConfig {\n const inheritance = this.parseInheritance(modeId);\n\n if (!inheritance.hasParent) {\n try {\n const mode = this.loadModeFile(modeId);\n this.activeMode = mode;\n logger.debug('SYSTEM', `Loaded mode: ${mode.name} (${modeId})`, undefined, {\n types: mode.observation_types.map(t => t.id),\n concepts: mode.observation_concepts.map(c => c.id)\n });\n return mode;\n } catch (error) {\n if (error instanceof Error) {\n logger.warn('WORKER', `Mode file not found: ${modeId}, falling back to 'code'`, { message: error.message });\n } else {\n logger.warn('WORKER', `Mode file not found: ${modeId}, falling back to 'code'`, { error: String(error) });\n }\n if (modeId === 'code') {\n throw new Error('Critical: code.json mode file missing');\n }\n return this.loadMode('code');\n }\n }\n\n const { parentId, overrideId } = inheritance;\n\n let parentMode: ModeConfig;\n try {\n parentMode = this.loadMode(parentId);\n } catch (error) {\n if (error instanceof Error) {\n logger.warn('WORKER', `Parent mode '${parentId}' not found for ${modeId}, falling back to 'code'`, { message: error.message });\n } else {\n logger.warn('WORKER', `Parent mode '${parentId}' not found for ${modeId}, falling back to 'code'`, { error: String(error) });\n }\n parentMode = this.loadMode('code');\n }\n\n let overrideConfig: Partial<ModeConfig>;\n try {\n overrideConfig = this.loadModeFile(overrideId);\n logger.debug('SYSTEM', `Loaded override file: ${overrideId} for parent ${parentId}`);\n } catch (error) {\n if (error instanceof Error) {\n logger.warn('WORKER', `Override file '${overrideId}' not found, using parent mode '${parentId}' only`, { message: error.message });\n } else {\n logger.warn('WORKER', `Override file '${overrideId}' not found, using parent mode '${parentId}' only`, { error: String(error) });\n }\n this.activeMode = parentMode;\n return parentMode;\n }\n\n if (!overrideConfig) {\n logger.warn('SYSTEM', `Invalid override file: ${overrideId}, using parent mode '${parentId}' only`);\n this.activeMode = parentMode;\n return parentMode;\n }\n\n const mergedMode = this.deepMerge(parentMode, overrideConfig);\n this.activeMode = mergedMode;\n\n logger.debug('SYSTEM', `Loaded mode with inheritance: ${mergedMode.name} (${modeId} = ${parentId} + ${overrideId})`, undefined, {\n parent: parentId,\n override: overrideId,\n types: mergedMode.observation_types.map(t => t.id),\n concepts: mergedMode.observation_concepts.map(c => c.id)\n });\n\n return mergedMode;\n }\n\n getActiveMode(): ModeConfig {\n if (!this.activeMode) {\n throw new Error('No mode loaded. Call loadMode() first.');\n }\n return this.activeMode;\n }\n\n getObservationTypes(): ObservationType[] {\n return this.getActiveMode().observation_types;\n }\n\n getTypeIcon(typeId: string): string {\n const type = this.getObservationTypes().find(t => t.id === typeId);\n return type?.emoji || '📝';\n }\n\n getWorkEmoji(typeId: string): string {\n const type = this.getObservationTypes().find(t => t.id === typeId);\n return type?.work_emoji || '📝';\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n\n// Server-beta-local copy of the worker provider error classification model.\n// Phase 5 anti-pattern guard: src/server/* must not import from\n// src/services/worker/*, so we duplicate the small, stable error model here.\n// Worker code keeps src/services/worker/provider-errors.ts unchanged.\n\nexport type ServerProviderErrorClass =\n | 'transient'\n | 'unrecoverable'\n | 'rate_limit'\n | 'quota_exhausted'\n | 'auth_invalid'\n | 'parse_error'\n | (string & {});\n\nexport class ServerClassifiedProviderError extends Error {\n readonly kind: ServerProviderErrorClass;\n readonly retryAfterMs?: number;\n readonly cause: unknown;\n\n constructor(\n message: string,\n opts: {\n kind: ServerProviderErrorClass;\n cause: unknown;\n retryAfterMs?: number;\n },\n ) {\n super(message);\n this.name = 'ServerClassifiedProviderError';\n this.kind = opts.kind;\n this.cause = opts.cause;\n if (opts.retryAfterMs !== undefined) {\n this.retryAfterMs = opts.retryAfterMs;\n }\n }\n}\n\n/**\n * Parse Retry-After header (seconds or HTTP-date). Returns ms or undefined.\n * Behavior intentionally mirrors the worker providers' helper so server\n * retries match worker retry policy.\n */\nexport function parseRetryAfterMs(value: string | null): number | undefined {\n if (!value) return undefined;\n const seconds = Number(value);\n if (!Number.isNaN(seconds) && seconds >= 0) {\n return Math.floor(seconds * 1000);\n }\n const dateMs = Date.parse(value);\n if (!Number.isNaN(dateMs)) {\n const delta = dateMs - Date.now();\n return delta > 0 ? delta : 0;\n }\n return undefined;\n}\n\ninterface ClassifyHttpInput {\n status?: number;\n bodyText?: string;\n headers?: Headers | { get(name: string): string | null };\n cause: unknown;\n providerLabel: string;\n}\n\n/**\n * Generic HTTP-error → ServerClassifiedProviderError mapping shared by\n * Gemini and OpenRouter server adapters. Provider-specific overrides (e.g.\n * Anthropic OverloadedError, Gemini quota body markers) are layered on top\n * by the per-provider classifier wrappers in this module.\n */\nexport function classifyHttpProviderError(input: ClassifyHttpInput): ServerClassifiedProviderError {\n const { status, providerLabel } = input;\n const body = input.bodyText ?? '';\n const lower = body.toLowerCase();\n const retryAfterMs = input.headers ? parseRetryAfterMs(input.headers.get('retry-after')) : undefined;\n const cause = status === undefined\n ? input.cause\n : new Error(`${providerLabel} HTTP error (status ${status})`);\n\n if (\n lower.includes('quota exceeded') ||\n lower.includes('insufficient credits') ||\n lower.includes('insufficient_quota') ||\n lower.includes('resource_exhausted')\n ) {\n return new ServerClassifiedProviderError(\n `${providerLabel} quota exhausted${status !== undefined ? ` (status ${status})` : ''}`,\n { kind: 'quota_exhausted', cause },\n );\n }\n\n if (status === 429) {\n return new ServerClassifiedProviderError(`${providerLabel} rate limit (429)`, {\n kind: 'rate_limit',\n cause,\n ...(retryAfterMs !== undefined ? { retryAfterMs } : {}),\n });\n }\n\n if (status === 401 || status === 403) {\n return new ServerClassifiedProviderError(`${providerLabel} auth error (status ${status})`, {\n kind: 'auth_invalid',\n cause,\n });\n }\n\n if (status === 400 || status === 404) {\n return new ServerClassifiedProviderError(`${providerLabel} bad request (status ${status})`, {\n kind: 'unrecoverable',\n cause,\n });\n }\n\n if (status !== undefined && status >= 500 && status < 600) {\n return new ServerClassifiedProviderError(`${providerLabel} upstream error (status ${status})`, {\n kind: 'transient',\n cause,\n });\n }\n\n if (status === undefined) {\n const message = input.cause instanceof Error ? input.cause.message : String(input.cause);\n return new ServerClassifiedProviderError(`${providerLabel} network error: ${message}`, {\n kind: 'transient',\n cause: input.cause,\n });\n }\n\n return new ServerClassifiedProviderError(\n `${providerLabel} API error (status ${status})`,\n { kind: 'unrecoverable', cause },\n );\n}\n","\nimport { logger } from './logger.js';\n\nconst TAG_NAMES = [\n 'private',\n 'claude-mem-context',\n 'system_instruction',\n 'system-instruction',\n 'persisted-output',\n 'system-reminder',\n] as const;\ntype TagName = (typeof TAG_NAMES)[number];\n\nconst STRIP_REGEX = new RegExp(\n `<(${TAG_NAMES.join('|')})\\\\b[^>]*>[\\\\s\\\\S]*?</\\\\1>`,\n 'g'\n);\n\nexport const SYSTEM_REMINDER_REGEX = /<system-reminder>[\\s\\S]*?<\\/system-reminder>/g;\n\nconst MAX_TAG_COUNT = 100;\n\nexport function stripTags(input: string): { stripped: string; counts: Record<TagName, number> } {\n const counts: Record<TagName, number> = Object.fromEntries(\n TAG_NAMES.map(name => [name, 0])\n ) as Record<TagName, number>;\n\n STRIP_REGEX.lastIndex = 0; \n\n let total = 0;\n const stripped = input.replace(STRIP_REGEX, (_, name: TagName) => {\n counts[name] = (counts[name] ?? 0) + 1;\n total += 1;\n return '';\n });\n\n if (total > MAX_TAG_COUNT) {\n logger.warn('SYSTEM', 'tag count exceeds limit', undefined, {\n tagCount: total,\n maxAllowed: MAX_TAG_COUNT,\n contentLength: input.length,\n });\n }\n\n return { stripped: stripped.trim(), counts };\n}\n\nexport function stripMemoryTagsFromJson(content: string): string {\n return stripTags(content).stripped;\n}\n\nexport function stripMemoryTagsFromPrompt(content: string): string {\n return stripTags(content).stripped;\n}\n\nconst PROTOCOL_ONLY_TAGS = ['task-notification'] as const;\n\nconst PROTOCOL_ONLY_REGEX = new RegExp(\n `^\\\\s*<(${PROTOCOL_ONLY_TAGS.join('|')})\\\\b[^>]*>(?:(?!<\\\\1\\\\b|</\\\\1\\\\b)[\\\\s\\\\S])*</\\\\1>\\\\s*$`,\n);\n\nconst MAX_PROTOCOL_PAYLOAD_BYTES = 256 * 1024;\n\nexport function isInternalProtocolPayload(text: string): boolean {\n if (!text) return false;\n if (text.length > MAX_PROTOCOL_PAYLOAD_BYTES) return false;\n return PROTOCOL_ONLY_REGEX.test(text);\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { ModeManager } from '../../../../services/domain/ModeManager.js';\nimport type { ModeConfig, ObservationType } from '../../../../services/domain/types.js';\nimport { stripTags } from '../../../../utils/tag-stripping.js';\nimport type { PostgresAgentEvent } from '../../../../storage/postgres/agent-events.js';\nimport type { ServerGenerationContext } from './types.js';\n\n// Fallback list mirrors the default observation types used by claude-mem\n// modes. The server-beta prompt does not strictly need a loaded mode file —\n// the parser accepts any of these as the <type> value — so when no mode is\n// loaded (tests, fresh installs) we synthesize a minimal type list rather\n// than throwing.\nconst FALLBACK_OBSERVATION_TYPES: ReadonlyArray<Pick<ObservationType, 'id'>> = [\n { id: 'discovery' },\n { id: 'progress' },\n { id: 'blocker' },\n { id: 'decision' },\n];\n\n// Build a single-shot generation prompt from a list of AgentEvent records\n// plus project/session metadata. Output: a user prompt asking the provider\n// to return one or more <observation> XML blocks (or an empty response if\n// the batch should be skipped). This is intentionally a single-turn request\n// — server-beta does NOT use the worker's multi-turn SDK conversation\n// model. parseAgentXml(...) accepts the response unchanged.\n//\n// Privacy: every event payload field passes through `stripTags` (which\n// removes <private>, <claude-mem-context>, <system-reminder>, etc.) before\n// being included in the prompt. Privacy enforcement here is belt-and-suspenders\n// — `processGeneratedResponse` also discards observations that are entirely\n// derived from privately-tagged inputs.\n\nexport interface BuildServerPromptResult {\n readonly prompt: string;\n readonly hadPrivateContent: boolean;\n readonly skippedAll: boolean;\n}\n\nconst MAX_PAYLOAD_CHARS = 16 * 1024;\n\nexport function buildServerGenerationPrompt(\n context: ServerGenerationContext,\n options: { mode?: ModeConfig } = {},\n): BuildServerPromptResult {\n const mode = options.mode ?? loadActiveModeOrFallback();\n\n let hadPrivateContent = false;\n let allEventsScrubbedToEmpty = true;\n const eventBlocks: string[] = [];\n\n for (const event of context.events) {\n const block = buildEventBlock(event);\n if (block.hadPrivate) {\n hadPrivateContent = true;\n }\n if (block.body.length > 0) {\n allEventsScrubbedToEmpty = false;\n eventBlocks.push(block.body);\n }\n }\n\n const skippedAll = context.events.length > 0 && allEventsScrubbedToEmpty;\n\n const sessionTag = context.project.serverSessionId\n ? `\\n <server_session_id>${escapeXml(context.project.serverSessionId)}</server_session_id>`\n : '';\n const projectTag = context.project.projectName\n ? `\\n <project_name>${escapeXml(context.project.projectName)}</project_name>`\n : '';\n\n const observationOutputSchema = buildObservationOutputSchema(mode);\n\n const prompt = [\n '<server_beta_observation_request>',\n ` <project_id>${escapeXml(context.project.projectId)}</project_id>`,\n ` <team_id>${escapeXml(context.project.teamId)}</team_id>` + sessionTag + projectTag,\n ` <generation_job_id>${escapeXml(context.job.id)}</generation_job_id>`,\n ' <agent_events>',\n eventBlocks.length > 0 ? eventBlocks.join('\\n') : ' <!-- empty after privacy stripping -->',\n ' </agent_events>',\n '</server_beta_observation_request>',\n '',\n 'You are observing an agent at work. Return one or more',\n '<observation>...</observation> XML blocks summarizing durable, useful',\n 'discoveries from the events above. If the events contain nothing worth',\n 'recording (e.g., everything was scrubbed by privacy filters or the',\n 'activity was trivial), return a single self-closing <skip_summary />',\n 'tag and nothing else. Do not include any prose outside the XML.',\n '',\n 'Schema for each <observation> block:',\n observationOutputSchema,\n ].join('\\n');\n\n return { prompt, hadPrivateContent, skippedAll };\n}\n\ninterface EventBlockResult {\n body: string;\n hadPrivate: boolean;\n}\n\nfunction buildEventBlock(event: PostgresAgentEvent): EventBlockResult {\n const rawPayload =\n typeof event.payload === 'string' ? event.payload : JSON.stringify(event.payload ?? {}, null, 2);\n\n const stripResult = stripTags(rawPayload);\n const hadPrivate = (stripResult.counts.private ?? 0) > 0;\n const truncatedPayload = stripResult.stripped.length > MAX_PAYLOAD_CHARS\n ? stripResult.stripped.slice(0, MAX_PAYLOAD_CHARS) + '\\n[...truncated]'\n : stripResult.stripped;\n\n if (truncatedPayload.trim().length === 0) {\n return { body: '', hadPrivate };\n }\n\n return {\n body: [\n ' <agent_event>',\n ` <id>${escapeXml(event.id)}</id>`,\n ` <event_type>${escapeXml(event.eventType)}</event_type>`,\n ` <source_adapter>${escapeXml(event.sourceAdapter)}</source_adapter>`,\n ` <occurred_at>${new Date(event.occurredAtEpoch).toISOString()}</occurred_at>`,\n ' <payload>',\n escapeXml(truncatedPayload),\n ' </payload>',\n ' </agent_event>',\n ].join('\\n'),\n hadPrivate,\n };\n}\n\nfunction loadActiveModeOrFallback(): ModeConfig | { observation_types: ReadonlyArray<Pick<ObservationType, 'id'>> } {\n try {\n return ModeManager.getInstance().getActiveMode();\n } catch {\n return { observation_types: FALLBACK_OBSERVATION_TYPES } as unknown as ModeConfig;\n }\n}\n\nfunction buildObservationOutputSchema(mode: ModeConfig | { observation_types: ReadonlyArray<Pick<ObservationType, 'id'>> }): string {\n const types = mode.observation_types.map(t => t.id).join(' | ');\n return [\n '<observation>',\n ` <type>[ ${types} ]</type>`,\n ' <title>...</title>',\n ' <subtitle>...</subtitle>',\n ' <facts><fact>...</fact></facts>',\n ' <narrative>...</narrative>',\n ' <concepts><concept>...</concept></concepts>',\n ' <files_read><file>...</file></files_read>',\n ' <files_modified><file>...</file></files_modified>',\n '</observation>',\n ].join('\\n');\n}\n\nfunction escapeXml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;');\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { logger } from '../../../utils/logger.js';\nimport {\n ServerClassifiedProviderError,\n parseRetryAfterMs,\n} from './shared/error-classification.js';\nimport { buildServerGenerationPrompt } from './shared/prompt-builder.js';\nimport type {\n ServerGenerationContext,\n ServerGenerationProvider,\n ServerGenerationResult,\n} from './shared/types.js';\n\nconst ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';\nconst ANTHROPIC_VERSION = '2023-06-01';\n// #2554 — the previous default `claude-3-5-sonnet-latest` is stale and 404s on\n// the current Anthropic Messages API. Align with the repo's canonical default\n// model (CLAUDE_MEM_MODEL in src/ui/viewer/constants/settings.ts and the\n// installer's model list) so a server with no explicit CLAUDE_MEM_SERVER_MODEL\n// generates against a valid model id instead of failing every job with a 404.\nexport const DEFAULT_SERVER_CLAUDE_MODEL = 'claude-sonnet-4-6';\nconst DEFAULT_MODEL = DEFAULT_SERVER_CLAUDE_MODEL;\n\nexport interface ClaudeObservationProviderOptions {\n apiKey: string;\n model?: string;\n maxOutputTokens?: number;\n fetchImpl?: typeof fetch;\n}\n\ninterface AnthropicMessagesResponse {\n content?: Array<{ type?: string; text?: string }>;\n usage?: { input_tokens?: number; output_tokens?: number };\n error?: { type?: string; message?: string };\n}\n\nexport class ClaudeObservationProvider implements ServerGenerationProvider {\n readonly providerLabel = 'claude' as const;\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxOutputTokens: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: ClaudeObservationProviderOptions) {\n if (!options.apiKey) {\n throw new ServerClassifiedProviderError('Anthropic API key not configured', {\n kind: 'auth_invalid',\n cause: new Error('apiKey is required'),\n });\n }\n this.apiKey = options.apiKey;\n this.model = options.model ?? DEFAULT_MODEL;\n this.maxOutputTokens = options.maxOutputTokens ?? 4096;\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async generate(\n context: ServerGenerationContext,\n signal?: AbortSignal,\n ): Promise<ServerGenerationResult> {\n const { prompt, skippedAll } = buildServerGenerationPrompt(context);\n if (skippedAll) {\n // All events were scrubbed by privacy stripping. Don't bill the\n // provider — return a synthetic skip response that parser accepts.\n return {\n rawText: '<skip_summary reason=\"all_events_private\" />',\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n\n let response: Response;\n try {\n response = await this.fetchImpl(ANTHROPIC_API_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': ANTHROPIC_VERSION,\n },\n body: JSON.stringify({\n model: this.model,\n max_tokens: this.maxOutputTokens,\n temperature: 0.3,\n messages: [{ role: 'user', content: prompt }],\n }),\n signal,\n });\n } catch (networkError) {\n throw classifyClaudeServerError({\n cause: networkError,\n });\n }\n\n if (!response.ok) {\n const bodyText = await safeReadBody(response);\n throw classifyClaudeServerError({\n status: response.status,\n bodyText,\n headers: response.headers,\n cause: new Error(`Anthropic API error: ${response.status} - ${bodyText}`),\n });\n }\n\n let data: AnthropicMessagesResponse;\n try {\n data = (await response.json()) as AnthropicMessagesResponse;\n } catch (parseError) {\n throw new ServerClassifiedProviderError('Anthropic returned invalid JSON', {\n kind: 'parse_error',\n cause: parseError,\n });\n }\n\n if (data.error) {\n throw classifyClaudeServerError({\n status: response.status,\n bodyText: `${data.error.type ?? ''} ${data.error.message ?? ''}`,\n headers: response.headers,\n cause: new Error(`Anthropic API error: ${data.error.type} - ${data.error.message}`),\n });\n }\n\n const blocks = Array.isArray(data.content) ? data.content : [];\n const rawText = blocks\n .filter(block => block?.type === 'text' && typeof block.text === 'string')\n .map(block => block.text!)\n .join('\\n')\n .trim();\n\n if (!rawText) {\n logger.warn('SDK', 'Anthropic returned empty content array', {\n provider: 'claude',\n model: this.model,\n });\n }\n\n const usage = data.usage ?? {};\n const tokensUsed =\n typeof usage.input_tokens === 'number' || typeof usage.output_tokens === 'number'\n ? (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0)\n : undefined;\n\n return {\n rawText,\n ...(tokensUsed !== undefined ? { tokensUsed } : {}),\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n}\n\ninterface ClassifyInput {\n status?: number;\n bodyText?: string;\n headers?: Headers | { get(name: string): string | null };\n cause: unknown;\n}\n\n/**\n * Anthropic-specific HTTP error classification. Mirrors worker\n * `classifyClaudeError`, but extracted for server-beta and rebound to\n * Anthropic Messages REST semantics rather than SDK error classes.\n */\nexport function classifyClaudeServerError(input: ClassifyInput): ServerClassifiedProviderError {\n const status = input.status;\n const body = input.bodyText ?? '';\n const lower = body.toLowerCase();\n const retryAfterMs = input.headers ? parseRetryAfterMs(input.headers.get('retry-after')) : undefined;\n\n if (lower.includes('overloaded')) {\n return new ServerClassifiedProviderError(\n `Anthropic overloaded${status !== undefined ? ` (status ${status})` : ''}`,\n { kind: 'transient', cause: input.cause },\n );\n }\n\n if (status === 401 || status === 403 || lower.includes('invalid api key')) {\n return new ServerClassifiedProviderError(\n `Anthropic auth invalid${status !== undefined ? ` (status ${status})` : ''}`,\n { kind: 'auth_invalid', cause: input.cause },\n );\n }\n\n if (status === 429) {\n return new ServerClassifiedProviderError('Anthropic rate limit (429)', {\n kind: 'rate_limit',\n cause: input.cause,\n ...(retryAfterMs !== undefined ? { retryAfterMs } : {}),\n });\n }\n\n if (lower.includes('quota exceeded')) {\n return new ServerClassifiedProviderError('Anthropic quota exhausted', {\n kind: 'quota_exhausted',\n cause: input.cause,\n });\n }\n\n if (\n lower.includes('prompt is too long') ||\n lower.includes('context window') ||\n lower.includes('max_tokens')\n ) {\n return new ServerClassifiedProviderError('Anthropic context overflow', {\n kind: 'unrecoverable',\n cause: input.cause,\n });\n }\n\n if (status === 529) {\n return new ServerClassifiedProviderError('Anthropic overloaded (529)', {\n kind: 'transient',\n cause: input.cause,\n });\n }\n\n if (status !== undefined && status >= 500 && status < 600) {\n return new ServerClassifiedProviderError(`Anthropic upstream error (status ${status})`, {\n kind: 'transient',\n cause: input.cause,\n });\n }\n\n if (status === 400) {\n return new ServerClassifiedProviderError('Anthropic bad request (400)', {\n kind: 'unrecoverable',\n cause: input.cause,\n });\n }\n\n if (status === undefined) {\n const message = input.cause instanceof Error ? input.cause.message : String(input.cause);\n return new ServerClassifiedProviderError(`Anthropic network error: ${message}`, {\n kind: 'transient',\n cause: input.cause,\n });\n }\n\n return new ServerClassifiedProviderError(\n `Anthropic API error: ${status}${body ? ` - ${body.substring(0, 200)}` : ''}`,\n { kind: 'unrecoverable', cause: input.cause },\n );\n}\n\nasync function safeReadBody(response: Response): Promise<string> {\n try {\n return await response.text();\n } catch {\n return '';\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { logger } from '../../../utils/logger.js';\nimport {\n ServerClassifiedProviderError,\n classifyHttpProviderError,\n parseRetryAfterMs,\n} from './shared/error-classification.js';\nimport { buildServerGenerationPrompt } from './shared/prompt-builder.js';\nimport type {\n ServerGenerationContext,\n ServerGenerationProvider,\n ServerGenerationResult,\n} from './shared/types.js';\n\nconst GEMINI_API_URL = 'https://generativelanguage.googleapis.com/v1/models';\nconst DEFAULT_MODEL = 'gemini-2.5-flash';\n\nexport interface GeminiObservationProviderOptions {\n apiKey: string;\n model?: string;\n maxOutputTokens?: number;\n fetchImpl?: typeof fetch;\n}\n\ninterface GeminiResponse {\n candidates?: Array<{\n content?: { parts?: Array<{ text?: string }> };\n }>;\n usageMetadata?: { totalTokenCount?: number };\n error?: { code?: number; status?: string; message?: string };\n}\n\nexport type GeminiBadRequestCategory =\n | 'role_sequence'\n | 'context_limit'\n | 'model_unsupported'\n | 'api_key'\n | 'unknown_bad_request';\n\nexport function categorizeGeminiBadRequest(bodyText: string): GeminiBadRequestCategory {\n const lower = bodyText.toLowerCase();\n\n if (\n lower.includes('api key not valid') ||\n lower.includes('api_key_invalid') ||\n lower.includes('api key expired') ||\n lower.includes('invalid api key')\n ) {\n return 'api_key';\n }\n\n if (\n lower.includes('please ensure that multiturn requests alternate') ||\n lower.includes('alternate between user and model') ||\n lower.includes('first content should be with role') ||\n (lower.includes('contents') && lower.includes('role') && (lower.includes('user') || lower.includes('model')))\n ) {\n return 'role_sequence';\n }\n\n if (\n lower.includes('context limit') ||\n lower.includes('context length') ||\n lower.includes('too many tokens') ||\n lower.includes('input is too long') ||\n lower.includes('prompt is too long') ||\n lower.includes('request payload size exceeds') ||\n (lower.includes('token') && (lower.includes('exceed') || lower.includes('maximum') || lower.includes('limit')))\n ) {\n return 'context_limit';\n }\n\n if (\n lower.includes('model not found') ||\n lower.includes('model_unsupported') ||\n lower.includes('unsupported model') ||\n lower.includes('not supported for generatecontent') ||\n lower.includes('not supported by this model') ||\n (lower.includes('model') && lower.includes('not supported')) ||\n (lower.includes('models/') && lower.includes('not found'))\n ) {\n return 'model_unsupported';\n }\n\n return 'unknown_bad_request';\n}\n\ninterface ClassifyGeminiServerErrorInput {\n status?: number;\n bodyText?: string;\n headers?: Headers | { get(name: string): string | null };\n cause: unknown;\n}\n\nfunction isQuotaBody(bodyText: string): boolean {\n const lower = bodyText.toLowerCase();\n return (\n lower.includes('quota exceeded') ||\n lower.includes('insufficient credits') ||\n lower.includes('insufficient_quota') ||\n lower.includes('resource_exhausted')\n );\n}\n\nexport function classifyGeminiServerError(input: ClassifyGeminiServerErrorInput): ServerClassifiedProviderError {\n const status = input.status;\n const bodyText = input.bodyText ?? '';\n\n if (status === 400 && !isQuotaBody(bodyText)) {\n const category = categorizeGeminiBadRequest(bodyText);\n return new ServerClassifiedProviderError(`Gemini bad request: ${category}`, {\n kind: 'unrecoverable',\n cause: new Error('Gemini HTTP error (status 400)'),\n });\n }\n\n return classifyHttpProviderError({\n ...input,\n providerLabel: 'Gemini',\n });\n}\n\nexport class GeminiObservationProvider implements ServerGenerationProvider {\n readonly providerLabel = 'gemini' as const;\n private readonly apiKey: string;\n private readonly model: string;\n private readonly maxOutputTokens: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: GeminiObservationProviderOptions) {\n if (!options.apiKey) {\n throw new ServerClassifiedProviderError('Gemini API key not configured', {\n kind: 'auth_invalid',\n cause: new Error('apiKey is required'),\n });\n }\n this.apiKey = options.apiKey;\n this.model = options.model ?? DEFAULT_MODEL;\n this.maxOutputTokens = options.maxOutputTokens ?? 4096;\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async generate(\n context: ServerGenerationContext,\n signal?: AbortSignal,\n ): Promise<ServerGenerationResult> {\n const { prompt, skippedAll } = buildServerGenerationPrompt(context);\n if (skippedAll) {\n return {\n rawText: '<skip_summary reason=\"all_events_private\" />',\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n\n const url = `${GEMINI_API_URL}/${encodeURIComponent(this.model)}:generateContent?key=${encodeURIComponent(this.apiKey)}`;\n\n let response: Response;\n try {\n response = await this.fetchImpl(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n contents: [{ role: 'user', parts: [{ text: prompt }] }],\n generationConfig: {\n temperature: 0.3,\n maxOutputTokens: this.maxOutputTokens,\n },\n }),\n signal,\n });\n } catch (networkError) {\n throw classifyGeminiServerError({\n cause: networkError,\n });\n }\n\n if (!response.ok) {\n const bodyText = await safeReadBody(response);\n throw classifyGeminiServerError({\n status: response.status,\n bodyText,\n headers: response.headers,\n cause: new Error(`Gemini HTTP error (status ${response.status})`),\n });\n }\n\n let data: GeminiResponse;\n try {\n data = (await response.json()) as GeminiResponse;\n } catch (parseError) {\n throw new ServerClassifiedProviderError('Gemini returned invalid JSON', {\n kind: 'parse_error',\n cause: parseError,\n });\n }\n\n if (data.error) {\n throw classifyGeminiServerError({\n status: response.status,\n bodyText: `${data.error.status ?? ''} ${data.error.message ?? ''}`,\n headers: response.headers,\n cause: new Error(`Gemini HTTP error (status ${response.status})`),\n });\n }\n\n const rawText = data.candidates?.[0]?.content?.parts?.[0]?.text?.trim() ?? '';\n if (!rawText) {\n logger.warn('SDK', 'Gemini returned empty content', { provider: 'gemini', model: this.model });\n }\n\n const tokensUsed = typeof data.usageMetadata?.totalTokenCount === 'number'\n ? data.usageMetadata.totalTokenCount\n : undefined;\n\n return {\n rawText,\n ...(tokensUsed !== undefined ? { tokensUsed } : {}),\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n}\n\n// Re-export for tests/auditing parity with worker classifier surface.\nexport { parseRetryAfterMs };\n\nasync function safeReadBody(response: Response): Promise<string> {\n try {\n return await response.text();\n } catch {\n return '';\n }\n}\n","// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Shared base-URL resolution for the OpenAI-compatible OpenRouter provider.\n *\n * Both the worker-runtime provider (src/services/worker/OpenRouterProvider.ts)\n * and the server-runtime provider\n * (src/server/generation/providers/OpenRouterObservationProvider.ts) use this\n * to turn the optional CLAUDE_MEM_OPENROUTER_BASE_URL setting into a concrete\n * `/chat/completions` endpoint. This makes the OpenRouter client a generic\n * OpenAI-compatible client.\n *\n * Closes #2382 (CLAUDE_MEM_OPENROUTER_BASE_URL), #2590 (custom provider with\n * configurable API base URL), #2622 (DeepSeek — OpenAI-compatible), and #2393\n * (LM Studio local model — OpenAI-compatible). Combined with the existing\n * CLAUDE_MEM_OPENROUTER_MODEL setting (passed verbatim), a user points the base\n * URL at any OpenAI-compatible endpoint and selects an arbitrary model id.\n *\n * Usage examples (set CLAUDE_MEM_PROVIDER=openrouter, then):\n *\n * DeepSeek:\n * CLAUDE_MEM_OPENROUTER_BASE_URL = https://api.deepseek.com\n * CLAUDE_MEM_OPENROUTER_MODEL = deepseek-chat\n * OPENROUTER_API_KEY (in ~/.claude-mem/.env) = <deepseek key>\n *\n * LM Studio (local, no key required):\n * CLAUDE_MEM_OPENROUTER_BASE_URL = http://localhost:1234/v1\n * CLAUDE_MEM_OPENROUTER_MODEL = <any local model id>\n *\n * Generic OpenAI-compatible endpoint:\n * CLAUDE_MEM_OPENROUTER_BASE_URL = https://my-gateway.example.com/v1\n * CLAUDE_MEM_OPENROUTER_MODEL = <model id>\n */\n\nexport const DEFAULT_OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';\n\nconst CHAT_COMPLETIONS_PATH = '/chat/completions';\n\n/**\n * Resolve the chat-completions endpoint from an optional configured base URL.\n *\n * Rules:\n * - unset/blank -> default OpenRouter chat-completions URL (behavior unchanged)\n * - a full URL already ending in `/chat/completions` -> used verbatim\n * - a base URL (e.g. `https://api.deepseek.com/v1`) -> `/chat/completions` appended\n * - trailing slashes are normalized before matching/appending\n */\nexport function resolveOpenRouterChatCompletionsUrl(baseUrl: string | undefined | null): string {\n const trimmed = (baseUrl ?? '').trim();\n if (!trimmed) {\n return DEFAULT_OPENROUTER_API_URL;\n }\n\n // Normalize trailing slashes so `.../v1/` and `.../v1` behave identically.\n const normalized = trimmed.replace(/\\/+$/, '');\n\n if (normalized.toLowerCase().endsWith(CHAT_COMPLETIONS_PATH)) {\n return normalized;\n }\n\n return `${normalized}${CHAT_COMPLETIONS_PATH}`;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { resolveOpenRouterChatCompletionsUrl } from '../../../shared/openrouter-base-url.js';\nimport { logger } from '../../../utils/logger.js';\nimport {\n ServerClassifiedProviderError,\n classifyHttpProviderError,\n} from './shared/error-classification.js';\nimport { buildServerGenerationPrompt } from './shared/prompt-builder.js';\nimport type {\n ServerGenerationContext,\n ServerGenerationProvider,\n ServerGenerationResult,\n} from './shared/types.js';\n\nconst DEFAULT_MODEL = 'anthropic/claude-3.5-sonnet';\n\nexport interface OpenRouterObservationProviderOptions {\n apiKey: string;\n model?: string;\n /**\n * Optional OpenAI-compatible base URL (#2382/#2590/#2622/#2393). When set,\n * requests POST to `<baseUrl>/chat/completions` (or verbatim if it already\n * ends in `/chat/completions`). When unset, the default OpenRouter endpoint\n * is used — behavior unchanged. Examples: https://api.deepseek.com (DeepSeek),\n * http://localhost:1234/v1 (LM Studio), a custom gateway base.\n */\n baseUrl?: string;\n maxOutputTokens?: number;\n siteUrl?: string;\n appName?: string;\n fetchImpl?: typeof fetch;\n}\n\ninterface OpenRouterResponse {\n choices?: Array<{ message?: { content?: string } }>;\n usage?: { total_tokens?: number };\n error?: { code?: string | number; message?: string };\n}\n\nexport class OpenRouterObservationProvider implements ServerGenerationProvider {\n readonly providerLabel = 'openrouter' as const;\n private readonly apiKey: string;\n private readonly model: string;\n private readonly apiUrl: string;\n private readonly maxOutputTokens: number;\n private readonly siteUrl: string;\n private readonly appName: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: OpenRouterObservationProviderOptions) {\n if (!options.apiKey) {\n throw new ServerClassifiedProviderError('OpenRouter API key not configured', {\n kind: 'auth_invalid',\n cause: new Error('apiKey is required'),\n });\n }\n this.apiKey = options.apiKey;\n // Model is passed verbatim so arbitrary OpenAI-compatible ids work. #2393.\n this.model = options.model ?? DEFAULT_MODEL;\n this.apiUrl = resolveOpenRouterChatCompletionsUrl(options.baseUrl);\n this.maxOutputTokens = options.maxOutputTokens ?? 4096;\n this.siteUrl = options.siteUrl ?? 'https://github.com/thedotmack/claude-mem';\n this.appName = options.appName ?? 'claude-mem';\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async generate(\n context: ServerGenerationContext,\n signal?: AbortSignal,\n ): Promise<ServerGenerationResult> {\n const { prompt, skippedAll } = buildServerGenerationPrompt(context);\n if (skippedAll) {\n return {\n rawText: '<skip_summary reason=\"all_events_private\" />',\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n\n let response: Response;\n try {\n response = await this.fetchImpl(this.apiUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'HTTP-Referer': this.siteUrl,\n 'X-Title': this.appName,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model: this.model,\n messages: [{ role: 'user', content: prompt }],\n temperature: 0.3,\n max_tokens: this.maxOutputTokens,\n }),\n signal,\n });\n } catch (networkError) {\n throw classifyHttpProviderError({\n cause: networkError,\n providerLabel: 'OpenRouter',\n });\n }\n\n if (!response.ok) {\n const bodyText = await safeReadBody(response);\n throw classifyHttpProviderError({\n status: response.status,\n bodyText,\n headers: response.headers,\n cause: new Error(`OpenRouter API error: ${response.status} - ${bodyText}`),\n providerLabel: 'OpenRouter',\n });\n }\n\n let data: OpenRouterResponse;\n try {\n data = (await response.json()) as OpenRouterResponse;\n } catch (parseError) {\n throw new ServerClassifiedProviderError('OpenRouter returned invalid JSON', {\n kind: 'parse_error',\n cause: parseError,\n });\n }\n\n if (data.error) {\n throw classifyHttpProviderError({\n status: response.status,\n bodyText: `${data.error.code ?? ''} ${data.error.message ?? ''}`,\n headers: response.headers,\n cause: new Error(`OpenRouter API error: ${data.error.code} - ${data.error.message}`),\n providerLabel: 'OpenRouter',\n });\n }\n\n const rawText = data.choices?.[0]?.message?.content?.trim() ?? '';\n if (!rawText) {\n logger.warn('SDK', 'OpenRouter returned empty content', {\n provider: 'openrouter',\n model: this.model,\n });\n }\n\n const tokensUsed = typeof data.usage?.total_tokens === 'number' ? data.usage.total_tokens : undefined;\n\n return {\n rawText,\n ...(tokensUsed !== undefined ? { tokensUsed } : {}),\n providerLabel: this.providerLabel,\n modelId: this.model,\n };\n }\n}\n\nasync function safeReadBody(response: Response): Promise<string> {\n try {\n return await response.text();\n } catch {\n return '';\n }\n}\n","\nimport { logger } from '../utils/logger.js';\nimport { ModeManager } from '../services/domain/ModeManager.js';\n\n// TODO(#2233): migrate to Anthropic tool-use API for deterministic JSON output. This text-XML path is the bridge.\n// Only strip fences when the entire payload is a single fenced block. Stripping\n// the first opening + last closing fence anywhere in the string can corrupt\n// content that contains internal fenced examples or surrounding prose\n// (CodeRabbit review on PR #2282).\nfunction stripCodeFences(text: string): string {\n const match = text.match(/^\\s*```(?:xml)?\\s*\\n([\\s\\S]*?)\\n```\\s*$/i);\n return match ? match[1] : text;\n}\n\nexport interface ParsedObservation {\n type: string;\n title: string | null;\n subtitle: string | null;\n facts: string[];\n narrative: string | null;\n concepts: string[];\n files_read: string[];\n files_modified: string[];\n}\n\nexport interface ParsedSummary {\n request: string | null;\n investigated: string | null;\n learned: string | null;\n completed: string | null;\n next_steps: string | null;\n notes: string | null;\n skipped?: boolean;\n skip_reason?: string | null;\n}\n\nexport type ParseResult =\n | { valid: true; observations: ParsedObservation[]; summary: ParsedSummary | null }\n | { valid: false };\n\nexport function parseAgentXml(raw: string, correlationId?: string | number): ParseResult {\n if (typeof raw !== 'string' || !raw.trim()) {\n return { valid: false };\n }\n\n raw = stripCodeFences(raw);\n\n const skipMatch = /<skip_summary(?:\\s+reason=\"([^\"]*)\")?\\s*\\/>/.exec(raw);\n if (skipMatch) {\n return {\n valid: true,\n observations: [],\n summary: {\n request: null,\n investigated: null,\n learned: null,\n completed: null,\n next_steps: null,\n notes: null,\n skipped: true,\n skip_reason: skipMatch[1] ?? null,\n },\n };\n }\n\n const firstRoot = /<(observation|summary)\\b/i.exec(raw);\n if (!firstRoot) {\n return { valid: false };\n }\n\n const rootName = firstRoot[1].toLowerCase();\n if (rootName === 'observation') {\n const observations = parseObservationBlocks(raw, correlationId);\n if (observations.length === 0) {\n return { valid: false };\n }\n return { valid: true, observations, summary: null };\n }\n\n const summary = parseSummaryBlock(raw, correlationId);\n if (!summary) {\n return { valid: false };\n }\n return { valid: true, observations: [], summary };\n}\n\nfunction parseObservationBlocks(text: string, correlationId?: string | number): ParsedObservation[] {\n const observations: ParsedObservation[] = [];\n\n const observationRegex = /<observation>([\\s\\S]*?)<\\/observation>/g;\n\n let match;\n while ((match = observationRegex.exec(text)) !== null) {\n const obsContent = match[1];\n\n const type = extractField(obsContent, 'type');\n const title = extractField(obsContent, 'title');\n const subtitle = extractField(obsContent, 'subtitle');\n const narrative = extractField(obsContent, 'narrative');\n const facts = extractArrayElements(obsContent, 'facts', 'fact');\n const concepts = extractArrayElements(obsContent, 'concepts', 'concept');\n const files_read = extractArrayElements(obsContent, 'files_read', 'file');\n const files_modified = extractArrayElements(obsContent, 'files_modified', 'file');\n\n const mode = ModeManager.getInstance().getActiveMode();\n const validTypes = mode.observation_types.map(t => t.id);\n const fallbackType = validTypes[0];\n let finalType = fallbackType;\n if (type) {\n if (validTypes.includes(type.trim())) {\n finalType = type.trim();\n } else {\n logger.error('PARSER', `Invalid observation type: ${type}, using \"${fallbackType}\"`, { correlationId });\n }\n } else {\n logger.error('PARSER', `Observation missing type field, using \"${fallbackType}\"`, { correlationId });\n }\n\n const cleanedConcepts = concepts.filter(c => c !== finalType);\n\n if (cleanedConcepts.length !== concepts.length) {\n logger.debug('PARSER', 'Removed observation type from concepts array', {\n correlationId,\n type: finalType,\n originalConcepts: concepts,\n cleanedConcepts\n });\n }\n\n if (!title && !narrative && facts.length === 0 && cleanedConcepts.length === 0) {\n logger.warn('PARSER', 'Skipping empty observation (all content fields null)', {\n correlationId,\n type: finalType\n });\n continue;\n }\n\n observations.push({\n type: finalType,\n title,\n subtitle,\n facts,\n narrative,\n concepts: cleanedConcepts,\n files_read,\n files_modified\n });\n }\n\n return observations;\n}\n\nfunction parseSummaryBlock(text: string, correlationId?: string | number): ParsedSummary | null {\n const summaryRegex = /<summary>([\\s\\S]*?)<\\/summary>/;\n const summaryMatch = summaryRegex.exec(text);\n if (!summaryMatch) return null;\n\n const summaryContent = summaryMatch[1];\n\n const request = extractField(summaryContent, 'request');\n const investigated = extractField(summaryContent, 'investigated');\n const learned = extractField(summaryContent, 'learned');\n const completed = extractField(summaryContent, 'completed');\n const next_steps = extractField(summaryContent, 'next_steps');\n const notes = extractField(summaryContent, 'notes'); \n\n if (!request && !investigated && !learned && !completed && !next_steps) {\n logger.warn('PARSER', 'Summary block has no sub-tags — rejecting false positive', { correlationId });\n return null;\n }\n\n return {\n request,\n investigated,\n learned,\n completed,\n next_steps,\n notes,\n };\n}\n\nfunction extractField(content: string, fieldName: string): string | null {\n const regex = new RegExp(`<${fieldName}>([\\\\s\\\\S]*?)</${fieldName}>`);\n const match = regex.exec(content);\n if (!match) return null;\n\n const trimmed = match[1].trim();\n return trimmed === '' ? null : trimmed;\n}\n\nfunction extractArrayElements(content: string, arrayName: string, elementName: string): string[] {\n const elements: string[] = [];\n\n const arrayRegex = new RegExp(`<${arrayName}>([\\\\s\\\\S]*?)</${arrayName}>`);\n const arrayMatch = arrayRegex.exec(content);\n\n if (!arrayMatch) {\n return elements;\n }\n\n const arrayContent = arrayMatch[1];\n\n const elementRegex = new RegExp(`<${elementName}>([\\\\s\\\\S]*?)</${elementName}>`, 'g');\n let elementMatch;\n while ((elementMatch = elementRegex.exec(arrayContent)) !== null) {\n const trimmed = elementMatch[1].trim();\n if (trimmed) {\n elements.push(trimmed);\n }\n }\n\n return elements;\n}\n","// SPDX-License-Identifier: Apache-2.0\n\nimport { parseAgentXml, type ParsedObservation, type ParsedSummary } from '../../sdk/parser.js';\nimport { logger } from '../../utils/logger.js';\nimport {\n PostgresObservationRepository,\n PostgresObservationSourcesRepository,\n buildObservationGenerationKey,\n type PostgresObservation,\n} from '../../storage/postgres/observations.js';\nimport {\n PostgresObservationGenerationJobEventsRepository,\n PostgresObservationGenerationJobRepository,\n type PostgresObservationGenerationJob,\n} from '../../storage/postgres/generation-jobs.js';\nimport { PostgresAuthRepository } from '../../storage/postgres/auth.js';\nimport { PostgresUsageRepository } from '../../storage/postgres/usage.js';\nimport {\n withPostgresTransaction,\n type PostgresPool,\n} from '../../storage/postgres/pool.js';\nimport { stripTags } from '../../utils/tag-stripping.js';\n\n// processGeneratedResponse owns the full \"we got XML from a provider →\n// persist + link + advance outbox\" pipeline. Every side-effect runs inside\n// a single Postgres transaction so retries are idempotent:\n//\n// - observations.generation_key (UNIQUE per team/project) collapses retry\n// duplicates to a single row.\n// - observation_sources (UNIQUE on observation_id, source_type, source_id)\n// collapses duplicate source links.\n// - observation_generation_jobs.transitionStatus is the lifecycle gate.\n//\n// The function NEVER touches worker SessionStore tables, NEVER assumes a\n// Claude Code transcript shape, and ALWAYS reloads the job before mutating.\n// BullMQ payload data is advisory; the outbox row is canonical.\n\nexport type ProcessGeneratedResponseOutcome =\n | {\n kind: 'completed';\n jobId: string;\n observations: PostgresObservation[];\n privateContentDetected: boolean;\n }\n | { kind: 'parse_error'; jobId: string; reason: string };\n\nexport interface ProcessGeneratedResponseInput {\n pool: PostgresPool;\n job: PostgresObservationGenerationJob;\n rawText: string;\n modelId?: string;\n providerLabel: string;\n workerId?: string;\n // Phase 11 — identity context propagated from the BullMQ payload (and\n // ultimately the API-key that ingested the source row). Persisted on\n // observation_sources.metadata for traceability and re-emitted in the\n // observation.created audit row.\n apiKeyId?: string | null;\n actorId?: string | null;\n sourceAdapter?: string | null;\n // Provider tokens this job spent (from the generate() result), for cost metering.\n tokensUsed?: number;\n}\n\nexport async function processGeneratedResponse(\n input: ProcessGeneratedResponseInput,\n): Promise<ProcessGeneratedResponseOutcome> {\n const { job, rawText } = input;\n\n const parsed = parseAgentXml(rawText, job.id);\n if (!parsed.valid) {\n return { kind: 'parse_error', jobId: job.id, reason: 'parser rejected response' };\n }\n\n // Skip-summary or zero-observation responses are still a success — the\n // provider explicitly decided there's nothing worth recording (e.g.\n // privacy-stripped batch). Mark the job completed with no observations.\n const observationsToWrite = parsed.observations ?? [];\n const skipped = parsed.summary?.skipped === true;\n const privateContentDetected = skipped || observationsToWrite.length === 0;\n\n const outcome = await withPostgresTransaction(input.pool, async (client) => {\n const obsRepo = new PostgresObservationRepository(client);\n const sourcesRepo = new PostgresObservationSourcesRepository(client);\n const jobsRepo = new PostgresObservationGenerationJobRepository(client);\n const eventsLogRepo = new PostgresObservationGenerationJobEventsRepository(client);\n const auditRepo = new PostgresAuthRepository(client);\n\n // Reload the job inside the transaction. If it was already completed\n // by another worker, return its existing observations idempotently.\n const fresh = await jobsRepo.getByIdForScope({\n id: job.id,\n projectId: job.projectId,\n teamId: job.teamId,\n });\n if (!fresh) {\n throw new Error(`generation job ${job.id} not found in scope`);\n }\n if (fresh.status === 'completed' || fresh.status === 'cancelled' || fresh.status === 'failed') {\n logger.info('SYSTEM', 'generation job already in terminal status; skipping persistence', {\n jobId: fresh.id,\n status: fresh.status,\n });\n return {\n kind: 'completed' as const,\n jobId: fresh.id,\n observations: [],\n privateContentDetected,\n };\n }\n\n const persisted: PostgresObservation[] = [];\n for (let index = 0; index < observationsToWrite.length; index++) {\n const parsedObservation = observationsToWrite[index]!;\n const content = renderObservationContent(parsedObservation);\n if (!content || content.trim().length === 0) {\n continue;\n }\n\n // Defense-in-depth: even if the parser slipped a private-tagged\n // string through, scrub before persisting.\n const scrubbed = stripTags(content);\n if (!scrubbed.stripped || scrubbed.stripped.trim().length === 0) {\n continue;\n }\n\n const generationKey = buildObservationGenerationKey({\n generationJobId: fresh.id,\n parsedObservationIndex: index,\n content: scrubbed.stripped,\n });\n\n const observation = await obsRepo.create({\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n serverSessionId: fresh.serverSessionId,\n kind: parsedObservation.type ?? 'observation',\n content: scrubbed.stripped,\n generationKey,\n metadata: {\n title: parsedObservation.title,\n subtitle: parsedObservation.subtitle,\n facts: parsedObservation.facts,\n narrative: parsedObservation.narrative,\n concepts: parsedObservation.concepts,\n files_read: parsedObservation.files_read,\n files_modified: parsedObservation.files_modified,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n },\n createdByJobId: fresh.id,\n });\n persisted.push(observation);\n\n await sourcesRepo.addSource({\n observationId: observation.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n sourceType: fresh.sourceType,\n sourceId: fresh.sourceId,\n agentEventId: fresh.agentEventId ?? null,\n generationJobId: fresh.id,\n metadata: {\n provider: input.providerLabel,\n parsedObservationIndex: index,\n // Phase 11 — denormalize identity context for traceability so an\n // operator can answer \"which api key produced this observation?\"\n // without joining back through generation_job → outbox → key.\n source_adapter: input.sourceAdapter ?? null,\n actor_id: input.actorId ?? null,\n api_key_id: input.apiKeyId ?? null,\n },\n });\n\n // Phase 11 — audit each generated observation. Using the SAME\n // generation_job_id reference so the audit chain (event_received →\n // generation_job.queued → generation_job.processing → observation.\n // created → observation.read) can be reconstructed.\n try {\n await auditRepo.createAuditLog({\n teamId: fresh.teamId,\n projectId: fresh.projectId,\n actorId: input.actorId ?? null,\n apiKeyId: input.apiKeyId ?? null,\n action: 'observation.created',\n resourceType: 'observation',\n resourceId: observation.id,\n details: {\n generationJobId: fresh.id,\n sourceType: fresh.sourceType,\n sourceId: fresh.sourceId,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n sourceAdapter: input.sourceAdapter ?? null,\n parsedObservationIndex: index,\n },\n });\n } catch (auditError) {\n logger.warn('SYSTEM', 'audit_log observation.created insert failed', {\n observationId: observation.id,\n error: auditError instanceof Error ? auditError.message : String(auditError),\n });\n }\n }\n\n // Advance outbox status. Phase 1 transitionStatus enforces legal\n // transitions and tenant scope inside its WHERE clause.\n await jobsRepo.transitionStatus({\n id: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n status: 'completed',\n });\n await eventsLogRepo.append({\n generationJobId: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n eventType: 'completed',\n statusAfter: 'completed',\n attempt: fresh.attempts,\n details: {\n provider: input.providerLabel,\n model: input.modelId ?? null,\n observationCount: persisted.length,\n privateContentDetected,\n workerId: input.workerId ?? null,\n },\n });\n\n // Audit log — best-effort; failure here would already be inside the\n // transaction so any insert error rolls everything back. We accept\n // that to keep the pipeline observable end-to-end.\n try {\n await auditRepo.createAuditLog({\n teamId: fresh.teamId,\n projectId: fresh.projectId,\n actorId: input.actorId ?? null,\n apiKeyId: input.apiKeyId ?? null,\n action: 'generation_job.completed',\n resourceType: 'observation_generation_job',\n resourceId: fresh.id,\n details: {\n generationJobId: fresh.id,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n observationCount: persisted.length,\n observationIds: persisted.map(o => o.id),\n sourceAdapter: input.sourceAdapter ?? null,\n },\n });\n } catch (auditError) {\n // The audit log table may not have a metadata column on older\n // schemas; swallow rather than failing generation.\n logger.warn('SYSTEM', 'audit log insert failed during generation', {\n jobId: fresh.id,\n error: auditError instanceof Error ? auditError.message : String(auditError),\n });\n }\n\n return {\n kind: 'completed' as const,\n jobId: fresh.id,\n observations: persisted,\n privateContentDetected,\n };\n });\n\n // Cost/usage metering — AFTER the transaction commits, so a metering insert\n // failure can NEVER roll back the observation + job writes (Greptile #3078: a\n // failed insert aborts the tx, and the catch can't un-abort it). Opt-in;\n // best-effort (logged); awaited so callers observe usage consistently.\n if (outcome.kind === 'completed' && process.env.CLAUDE_MEM_USAGE_METERING === '1') {\n try {\n const usageRepo = new PostgresUsageRepository(input.pool);\n if (input.tokensUsed && input.tokensUsed > 0) {\n await usageRepo.record({\n teamId: input.job.teamId,\n projectId: input.job.projectId,\n kind: 'tokens',\n quantity: input.tokensUsed,\n metadata: { jobId: input.job.id, provider: input.providerLabel, model: input.modelId ?? null },\n });\n }\n if (outcome.observations.length > 0) {\n await usageRepo.record({\n teamId: input.job.teamId,\n projectId: input.job.projectId,\n kind: 'observation',\n quantity: outcome.observations.length,\n metadata: { jobId: input.job.id },\n });\n }\n } catch (usageError) {\n logger.warn('SYSTEM', 'usage metering record failed (post-commit)', {\n jobId: input.job.id,\n error: usageError instanceof Error ? usageError.message : String(usageError),\n });\n }\n }\n return outcome;\n}\n\nexport interface MarkGenerationFailedInput {\n pool: PostgresPool;\n job: PostgresObservationGenerationJob;\n reason: string;\n classification?: string;\n retryable: boolean;\n workerId?: string;\n}\n\n/**\n * Move a generation job to a non-success terminal state. Used when the\n * provider returned an error or invalid XML. Retryable failures move the\n * job back to `queued` so reconciliation can re-enqueue; non-retryable\n * failures move to `failed`.\n */\nexport async function markGenerationFailed(input: MarkGenerationFailedInput): Promise<void> {\n await withPostgresTransaction(input.pool, async (client) => {\n const jobsRepo = new PostgresObservationGenerationJobRepository(client);\n const eventsLogRepo = new PostgresObservationGenerationJobEventsRepository(client);\n\n const fresh = await jobsRepo.getByIdForScope({\n id: input.job.id,\n projectId: input.job.projectId,\n teamId: input.job.teamId,\n });\n if (!fresh || fresh.status === 'completed' || fresh.status === 'cancelled') {\n return;\n }\n\n const canRetry = input.retryable && fresh.attempts < fresh.maxAttempts;\n const target = canRetry ? 'queued' : 'failed';\n\n await jobsRepo.transitionStatus({\n id: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n status: target,\n lastError: { reason: input.reason, classification: input.classification ?? null },\n ...(canRetry ? { nextAttemptAt: new Date(Date.now() + retryDelayMs(fresh.attempts)) } : {}),\n });\n\n await eventsLogRepo.append({\n generationJobId: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n eventType: canRetry ? 'retry_scheduled' : 'failed',\n statusAfter: target,\n attempt: fresh.attempts,\n details: {\n reason: input.reason,\n classification: input.classification ?? null,\n workerId: input.workerId ?? null,\n },\n });\n });\n}\n\n/**\n * Persist a parsed session summary as an observations row with kind='summary'.\n *\n * Wraps the same outbox transition / source-link / audit pipeline as\n * processGeneratedResponse but emits a single 'summary'-kind observation\n * derived from the summary fields. Idempotency is enforced through the same\n * `observations.generation_key` UNIQUE index — re-running the summary job\n * after a restart will collapse to one row.\n */\nexport async function processSessionSummaryResponse(\n input: ProcessGeneratedResponseInput,\n): Promise<ProcessGeneratedResponseOutcome> {\n const { job, rawText } = input;\n\n if (job.sourceType !== 'session_summary') {\n return { kind: 'parse_error', jobId: job.id, reason: 'session summary processor invoked on non-summary job' };\n }\n\n const parsed = parseAgentXml(rawText, job.id);\n if (!parsed.valid) {\n return { kind: 'parse_error', jobId: job.id, reason: 'parser rejected summary response' };\n }\n\n const summary = parsed.summary ?? null;\n const skipped = summary?.skipped === true;\n const summaryContent = summary ? renderSummaryContent(summary) : '';\n const privateContentDetected = skipped || summaryContent.trim().length === 0;\n\n return await withPostgresTransaction(input.pool, async (client) => {\n const obsRepo = new PostgresObservationRepository(client);\n const sourcesRepo = new PostgresObservationSourcesRepository(client);\n const jobsRepo = new PostgresObservationGenerationJobRepository(client);\n const eventsLogRepo = new PostgresObservationGenerationJobEventsRepository(client);\n const auditRepo = new PostgresAuthRepository(client);\n\n const fresh = await jobsRepo.getByIdForScope({\n id: job.id,\n projectId: job.projectId,\n teamId: job.teamId,\n });\n if (!fresh) {\n throw new Error(`session summary generation job ${job.id} not found in scope`);\n }\n if (fresh.status === 'completed' || fresh.status === 'cancelled' || fresh.status === 'failed') {\n logger.info('SYSTEM', 'session summary job already in terminal status; skipping persistence', {\n jobId: fresh.id,\n status: fresh.status,\n });\n return {\n kind: 'completed' as const,\n jobId: fresh.id,\n observations: [],\n privateContentDetected,\n };\n }\n\n const persisted: PostgresObservation[] = [];\n if (!privateContentDetected) {\n const scrubbed = stripTags(summaryContent);\n const scrubbedContent = scrubbed.stripped ?? '';\n if (scrubbedContent.trim().length > 0) {\n const generationKey = buildObservationGenerationKey({\n generationJobId: fresh.id,\n parsedObservationIndex: 0,\n content: scrubbedContent,\n });\n const observation = await obsRepo.create({\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n serverSessionId: fresh.serverSessionId,\n kind: 'summary',\n content: scrubbedContent,\n generationKey,\n metadata: {\n request: summary?.request ?? null,\n investigated: summary?.investigated ?? null,\n learned: summary?.learned ?? null,\n completed: summary?.completed ?? null,\n next_steps: summary?.next_steps ?? null,\n notes: summary?.notes ?? null,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n },\n createdByJobId: fresh.id,\n });\n persisted.push(observation);\n\n await sourcesRepo.addSource({\n observationId: observation.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n sourceType: 'session_summary',\n sourceId: fresh.sourceId,\n generationJobId: fresh.id,\n metadata: {\n provider: input.providerLabel,\n parsedObservationIndex: 0,\n source_adapter: input.sourceAdapter ?? null,\n actor_id: input.actorId ?? null,\n api_key_id: input.apiKeyId ?? null,\n },\n });\n\n // Phase 11 — observation.created audit for the summary observation.\n try {\n await auditRepo.createAuditLog({\n teamId: fresh.teamId,\n projectId: fresh.projectId,\n actorId: input.actorId ?? null,\n apiKeyId: input.apiKeyId ?? null,\n action: 'observation.created',\n resourceType: 'observation',\n resourceId: observation.id,\n details: {\n generationJobId: fresh.id,\n sourceType: 'session_summary',\n sourceId: fresh.sourceId,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n sourceAdapter: input.sourceAdapter ?? null,\n kind: 'summary',\n },\n });\n } catch (auditError) {\n logger.warn('SYSTEM', 'audit_log observation.created (summary) insert failed', {\n observationId: observation.id,\n error: auditError instanceof Error ? auditError.message : String(auditError),\n });\n }\n }\n }\n\n await jobsRepo.transitionStatus({\n id: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n status: 'completed',\n });\n await eventsLogRepo.append({\n generationJobId: fresh.id,\n projectId: fresh.projectId,\n teamId: fresh.teamId,\n eventType: 'completed',\n statusAfter: 'completed',\n attempt: fresh.attempts,\n details: {\n provider: input.providerLabel,\n model: input.modelId ?? null,\n observationCount: persisted.length,\n privateContentDetected,\n workerId: input.workerId ?? null,\n sourceType: 'session_summary',\n },\n });\n\n try {\n await auditRepo.createAuditLog({\n teamId: fresh.teamId,\n projectId: fresh.projectId,\n actorId: input.actorId ?? null,\n apiKeyId: input.apiKeyId ?? null,\n action: 'generation_job.completed',\n resourceType: 'observation_generation_job',\n resourceId: fresh.id,\n details: {\n generationJobId: fresh.id,\n provider: input.providerLabel,\n model: input.modelId ?? null,\n observationCount: persisted.length,\n observationIds: persisted.map(o => o.id),\n sourceAdapter: input.sourceAdapter ?? null,\n sourceType: 'session_summary',\n },\n });\n } catch (auditError) {\n logger.warn('SYSTEM', 'audit log insert failed during summary generation', {\n jobId: fresh.id,\n error: auditError instanceof Error ? auditError.message : String(auditError),\n });\n }\n\n return {\n kind: 'completed' as const,\n jobId: fresh.id,\n observations: persisted,\n privateContentDetected,\n };\n });\n}\n\nfunction renderSummaryContent(summary: ParsedSummary): string {\n const parts: string[] = [];\n if (summary.request) parts.push(`Request: ${summary.request}`);\n if (summary.investigated) parts.push(`Investigated: ${summary.investigated}`);\n if (summary.learned) parts.push(`Learned: ${summary.learned}`);\n if (summary.completed) parts.push(`Completed: ${summary.completed}`);\n if (summary.next_steps) parts.push(`Next steps: ${summary.next_steps}`);\n if (summary.notes) parts.push(`Notes: ${summary.notes}`);\n return parts.join('\\n\\n').trim();\n}\n\nfunction renderObservationContent(observation: ParsedObservation): string {\n const parts: string[] = [];\n if (observation.title) parts.push(observation.title);\n if (observation.subtitle) parts.push(observation.subtitle);\n if (observation.narrative) parts.push(observation.narrative);\n if (observation.facts && observation.facts.length > 0) {\n parts.push(observation.facts.map(f => `- ${f}`).join('\\n'));\n }\n return parts.join('\\n\\n').trim();\n}\n\nfunction retryDelayMs(attempts: number): number {\n // Exponential backoff: 5s, 25s, 125s, capped at 10 minutes.\n const base = 5000 * Math.pow(5, Math.max(0, attempts));\n return Math.min(base, 10 * 60 * 1000);\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["fs","path","os","execFile","spawn","promisify","path","existsSync","mkdirSync","readFileSync","join","LogLevel","existsSync","mkdirSync","join","readFileSync","readFileSync","existsSync","mkdirSync","join","dirname","homedir","join","homedir","existsSync","dirname","mkdirSync","readFileSync","existsSync","readFileSync","rmSync","existsSync","mkdirSync","readFileSync","writeFileSync","readFileSync","mkdirSync","existsSync","writeFileSync","existsSync","readFileSync","record","existsSync","readFileSync","PID_FILE","PID_FILE","existsSync","readFileSync","rmSync","execFileAsync","promisify","execFile","spawn","path","sep","readFileSync","writeFileSync","mkdirSync","existsSync","join","join","path","existsSync","readFileSync","mkdirSync","writeFileSync","createHash","createHash","readFileSync","existsSync","join","join","existsSync","readFileSync","DEFAULT_MODEL","safeReadBody","safeReadBody","DEFAULT_MODEL","safeReadBody"]}