@happyvertical/smrt-core 0.53.3 → 0.53.5

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.
Files changed (68) hide show
  1. package/AGENTS.md +4 -0
  2. package/README.md +47 -0
  3. package/agents/build-knowledge.md +17 -0
  4. package/agents/generators.md +24 -1
  5. package/dist/class.d.ts.map +1 -1
  6. package/dist/class.js +29 -5
  7. package/dist/class.js.map +1 -1
  8. package/dist/generated-collection-access.d.ts +43 -0
  9. package/dist/generated-collection-access.d.ts.map +1 -0
  10. package/dist/generated-collection-access.js +71 -0
  11. package/dist/generated-collection-access.js.map +1 -0
  12. package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
  13. package/dist/generators/mcp-runtime-template.js +8 -5
  14. package/dist/generators/mcp-runtime-template.js.map +1 -1
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +3 -2
  18. package/dist/manifest/static-manifest.js +1 -1
  19. package/dist/manifest/static-manifest.js.map +1 -1
  20. package/dist/manifest/store.js +1 -1
  21. package/dist/manifest.json +1 -1
  22. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  23. package/dist/scanner/manifest-generator.js +3 -1
  24. package/dist/scanner/manifest-generator.js.map +1 -1
  25. package/dist/smrt-knowledge.json +10 -9
  26. package/dist/utils/qualified-names.d.ts +15 -0
  27. package/dist/utils/qualified-names.d.ts.map +1 -1
  28. package/dist/utils/qualified-names.js +31 -1
  29. package/dist/utils/qualified-names.js.map +1 -1
  30. package/dist/vite-plugin/changes-route.d.ts +4 -3
  31. package/dist/vite-plugin/changes-route.d.ts.map +1 -1
  32. package/dist/vite-plugin/changes-route.js +8 -7
  33. package/dist/vite-plugin/changes-route.js.map +1 -1
  34. package/dist/vite-plugin/dev-plane-route.d.ts.map +1 -1
  35. package/dist/vite-plugin/dev-plane-route.js +4 -4
  36. package/dist/vite-plugin/dev-plane-route.js.map +1 -1
  37. package/dist/vite-plugin/events-route.d.ts.map +1 -1
  38. package/dist/vite-plugin/events-route.js +4 -4
  39. package/dist/vite-plugin/events-route.js.map +1 -1
  40. package/dist/vite-plugin/index.d.ts.map +1 -1
  41. package/dist/vite-plugin/index.js +21 -7
  42. package/dist/vite-plugin/index.js.map +1 -1
  43. package/dist/vite-plugin/resources-route.d.ts.map +1 -1
  44. package/dist/vite-plugin/resources-route.js +9 -4
  45. package/dist/vite-plugin/resources-route.js.map +1 -1
  46. package/dist/vite-plugin/sveltekit-config-import.d.ts +22 -0
  47. package/dist/vite-plugin/sveltekit-config-import.d.ts.map +1 -1
  48. package/dist/vite-plugin/sveltekit-config-import.js +44 -2
  49. package/dist/vite-plugin/sveltekit-config-import.js.map +1 -1
  50. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  51. package/dist/vite-plugin/sveltekit-generator.js +59 -9
  52. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  53. package/dist/vite-plugin/sveltekit-register-injection.d.ts +26 -0
  54. package/dist/vite-plugin/sveltekit-register-injection.d.ts.map +1 -0
  55. package/dist/vite-plugin/sveltekit-register-injection.js +37 -0
  56. package/dist/vite-plugin/sveltekit-register-injection.js.map +1 -0
  57. package/dist/vite-plugin/sync-apply-route.d.ts +1 -1
  58. package/dist/vite-plugin/sync-apply-route.d.ts.map +1 -1
  59. package/dist/vite-plugin/sync-apply-route.js +4 -4
  60. package/dist/vite-plugin/sync-apply-route.js.map +1 -1
  61. package/dist/vite-preset/index.d.ts +65 -0
  62. package/dist/vite-preset/index.d.ts.map +1 -0
  63. package/dist/vite-preset/index.js +105 -0
  64. package/dist/vite-preset/index.js.map +1 -0
  65. package/dist/vite.d.ts +5 -0
  66. package/dist/vite.d.ts.map +1 -0
  67. package/dist/vite.js +2 -0
  68. package/package.json +15 -10
package/AGENTS.md CHANGED
@@ -105,6 +105,10 @@ and repository rules.
105
105
  a resolved table that differs first (#3106). Stack
106
106
  attribution skips smrt-core (including source-mapped installed
107
107
  `smrt-core/src` frames, #3109), decorator-helper, and module-runner frames.
108
+ Qualified names are `@scope/pkg:Class`; the scope is a deliberate
109
+ discriminator, so manifest generation (`generateManifest`,
110
+ `applyGenerationPasses`, hence `pnpm build`) throws
111
+ `CONFIG_UNSCOPED_PACKAGE_NAME` for an unscoped `package.json` name (#3408).
108
112
  Instance and collection registry lookups pass the qualified name. The schema
109
113
  planner rejects a table claimed by two unrelated classes
110
114
  (`CONFIG_TABLE_NAME_COLLISION`): only one STI family shares a table (#3098);
package/README.md CHANGED
@@ -180,6 +180,41 @@ export default defineConfig({
180
180
  });
181
181
  ```
182
182
 
183
+ #### One-call preset for SvelteKit apps
184
+
185
+ `smrt()` applies the Oxc decorator config and composes `smrtConsumer()` and
186
+ `smrtPlugin()` with the SvelteKit conventions (`src/lib/objects`,
187
+ `src/lib/types/smrt-generated`, `src/routes/api`, `src/lib/server/smrt.ts`):
188
+
189
+ ```typescript
190
+ // vite.config.ts
191
+ import { sveltekit } from '@sveltejs/kit/vite';
192
+ import { smrt } from '@happyvertical/smrt-core/vite';
193
+ import { defineConfig } from 'vite';
194
+
195
+ export default defineConfig({ plugins: [sveltekit(), smrt()] });
196
+ ```
197
+
198
+ ```typescript
199
+ // smrt.config.ts: the declared, deterministic list of consumed packages.
200
+ // Tooling and UI packages are never inferred from package.json.
201
+ export default { consumer: { packages: ['@happyvertical/smrt-users'] } };
202
+ ```
203
+
204
+ `smrt({ packages })` overrides the config list; `[]` declares that none are
205
+ consumed. A missing or malformed list fails the build. `objectsDir`, `typesDir`,
206
+ `routesDir`, `configPath`, `configFileName`, `include`, `exclude` and
207
+ `projectRoot` override the conventions; `decorators: false` omits the decorator
208
+ config (values you set under `oxc.decorator` are never overwritten). `smrt()`
209
+ returns a promise, which Vite accepts in `plugins`. The plugin also writes the
210
+ `virtual-modules.d.ts` ambient declarations (into `typesDir`) whenever it runs
211
+ a build or whenever Vite resolves the config, including `svelte-kit sync`
212
+ (SvelteKit 2.69.3+ resolves the Vite config there), not only under a dev server.
213
+ `svelte-kit sync && tsc` therefore passes on a clean clone with no prior build
214
+ and no copied shim; the file is rewritten
215
+ only when its contents change. The two plugins remain
216
+ public for custom setups.
217
+
183
218
  ```bash
184
219
  pnpm vite build
185
220
  pnpm smrt db:migrate
@@ -298,6 +333,17 @@ Enable SvelteKit route generation with `svelteKit: { enabled: true }`. Its
298
333
  default output directory is `src/routes/api`; set `svelteKit.routesDir` when
299
334
  your application uses a different route root.
300
335
 
336
+ Generated routes do not need a hand-written `getCollection`. Each route embeds
337
+ a prelude that imports the generated `smrt-register` module and the app's
338
+ config module (`svelteKit.configPath`/`configFileName`, `$lib/server/smrt` by
339
+ default), then resolves collections through that module's exported `runtime`
340
+ (`createSmrtSvelteKitRuntime()` from `@happyvertical/smrt-app-runtime/sveltekit`,
341
+ whose `getCollection()` is request-scoped). A `getCollection`/`getSmrtConfig`
342
+ export there still takes precedence for one release, with a deprecation
343
+ warning. The plugin also prepends the registration import to that config
344
+ module, and generation refuses a `configFileName` that names the registration
345
+ module (`smrt-register.*`).
346
+
301
347
  A consumer can host selected dependency models with the same generator. This is
302
348
  an explicit HTTP boundary that is separate from the broader `packages`
303
349
  registration inventory. Name exact provider-qualified object references; a
@@ -473,6 +519,7 @@ file has run.
473
519
  | --- | --- |
474
520
  | `@happyvertical/smrt-core` | Objects, collections, decorators, registry, configuration |
475
521
  | `@happyvertical/smrt-core/vite-plugin` | Manifest, route, client, and knowledge generation |
522
+ | `@happyvertical/smrt-core/vite` | One-call preset: decorators, consumer, and producer plugins |
476
523
  | `@happyvertical/smrt-core/consumer-plugin` | Consume manifests from installed s-m-r-t packages |
477
524
  | `@happyvertical/smrt-core/generators` | REST, OpenAPI, CLI, and MCP generator APIs |
478
525
  | `@happyvertical/smrt-core/manifest` | Runtime manifest loading and inspection |
@@ -101,6 +101,13 @@ retains legacy filename inference. Vitest selects dev mode explicitly, so tests
101
101
  never replace a production `dist/manifest.json`. Relative output directories are
102
102
  resolved against the builder's project root.
103
103
 
104
+ With `svelteKit.enabled`, `smrtPlugin()`'s transform prepends
105
+ `import './smrt-register.js';` to the configured config module
106
+ (`<configPath>/<configFileName>`, `src/vite-plugin/sveltekit-register-injection.ts`)
107
+ once the register module exists, on the same first line so line numbers hold.
108
+ Apps therefore never hand-write the guarded registration import (#3416); a
109
+ config module that still has it imports the same module twice, harmlessly.
110
+
104
111
  Tracked `src/lib/server/smrt-register.ts` belongs to SvelteKit config/type
105
112
  generation. Generic library builds consume it but do not cache or restore it
106
113
  as an output; otherwise a concurrent config producer's bytes can be replayed
@@ -117,6 +124,16 @@ admin auth.
117
124
 
118
125
  ## Vite Plugin
119
126
 
127
+ `src/vite-preset/` (`@happyvertical/smrt-core/vite`, `smrt()`) composes the Oxc
128
+ decorator block, `smrtConsumer`, then `smrtPlugin` with the template path
129
+ conventions. Its package list comes only from the `smrt({ packages })` option, or
130
+ `smrt.config` `consumer.packages` (smrt-config schema); it never sniffs
131
+ `package.json`, and a missing or malformed list throws. Keep its output
132
+ byte-identical to the two-plugin form (`src/vite-preset/index.test.ts`). It
133
+ also dedupes `@sveltejs/kit` (`smrt:shared-runtime`): SvelteKit matches
134
+ `redirect()`/`fail()` by class, and a linked/workspace SMRT SvelteKit entry
135
+ would otherwise import its own copy and turn redirects into 500s.
136
+
120
137
  ```typescript
121
138
  // vite.config.ts — required for @smrt() decorators (Vite 8+, oxc transform)
122
139
  export default defineConfig({
@@ -234,7 +234,8 @@ only when `svelteKit.devPlaneRoute.enabled === true` and
234
234
  resolvability guard as `_resources`). The route is dev-only (`$app/environment`
235
235
  `dev`, else 404), refuses to start without `SMRT_DEV_MCP_TOKEN` (503), and
236
236
  delegates everything else to `createDevPlane`, passing `getSmrtConfig(...).db`
237
- from the generated `$lib/server/smrt` so the plane shares the app's connection.
237
+ from the embedded access prelude (below) so the plane shares the app's
238
+ connection.
238
239
  Core never imports smrt-dev-mcp; only the generated file does, at consumer
239
240
  runtime. The route carries the auto-generated header so the sweep owns it.
240
241
 
@@ -266,3 +267,25 @@ class, action, and raw generated name to in-process catalog composers. App MCP
266
267
  uses it for class allow-lists, tenant checks, and compatible workflow guards;
267
268
  those consumers must not reverse protocol aliases. The alias remains the sole
268
269
  wire tool name and is still the key for public patterns and generic tool policy.
270
+
271
+ ## Generated route collection access (#3416)
272
+
273
+ There is no generated route-access module. Every generated SvelteKit route
274
+ that needs collections (CRUD, custom actions, `_changes`, `_events`,
275
+ `sync/apply`, `_dev`) embeds the prelude from
276
+ `generateCollectionAccessImports()` (`sveltekit-config-import.ts`): it imports
277
+ the generated `smrt-register` module, then the app's config module
278
+ (`$lib/server/smrt` by default) as `* as smrtApplication`, and destructures
279
+ `getCollection`/`getSmrtConfig` from `createGeneratedCollectionAccess()`
280
+ (`src/generated-collection-access.ts`). Generation fails before writing
281
+ anything when `configFileName` resolves to the registration module
282
+ (`smrt-register` with any script extension or case).
283
+ That helper resolves per call, never caching: a legacy `getCollection` /
284
+ `getSmrtConfig` export wins (deprecated, warned once per module, for one
285
+ release), else the exported app-runtime `runtime.getCollection()` /
286
+ `classOptions()`, else it fails with an error naming the module. `_resources`
287
+ and static collection-scoped actions only need registration, so they import
288
+ `smrt-register` and then the config module. When the config module is missing
289
+ and `@happyvertical/smrt-app-runtime/sveltekit` resolves from the consumer, the
290
+ generator writes a runtime-shaped default instead of the legacy
291
+ `getCollection` file.
@@ -1 +1 @@
1
- {"version":3,"file":"class.d.ts","sourceRoot":"","sources":["../src/class.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"class.d.ts","sourceRoot":"","sources":["../src/class.ts"],"names":[],"mappings":"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"}
package/dist/class.js CHANGED
@@ -14,6 +14,7 @@ import { ensureDeferredSystemTableCompatibility, tableExists } from "./system/co
14
14
  import { ensureSystemTables } from "./system/bootstrap.js";
15
15
  import { createLogger } from "@happyvertical/logger";
16
16
  import { getDatabase } from "@happyvertical/sql";
17
+ import { getAIConfigBlock, mergeAIConfigObjects, tryResolveAIProviderConfig, withAIAliases } from "@happyvertical/smrt-config";
17
18
  //#region src/class.ts
18
19
  /**
19
20
  * `_smrt_migrations.version` suffix marking that the deferred (manifest-created)
@@ -307,15 +308,14 @@ var SmrtClass = class SmrtClass {
307
308
  const globalConfig = config.toJSON();
308
309
  const usageConfig = this.mergeAiUsageConfig(globalConfig);
309
310
  this.initializeAiUsageHandlers(usageConfig);
310
- if (!this._ai && (this.options.ai || globalConfig.ai || process.env.SMRT_AI_PROVIDER)) {
311
+ const declaredBlock = getAIConfigBlock();
312
+ const aiConfigBlock = declaredBlock?.provider ? declaredBlock : null;
313
+ if (!this._ai && (this.options.ai || globalConfig.ai || aiConfigBlock || process.env.SMRT_AI_PROVIDER)) {
311
314
  const aiOption = this.options.ai;
312
315
  if (aiOption && typeof aiOption === "object" && typeof aiOption.embed === "function" && !aiOption.provider) this._ai = aiOption;
313
316
  else {
314
317
  const { loadEnvConfig } = await import("@happyvertical/utils");
315
- const userConfig = {
316
- ...globalConfig.ai || {},
317
- ...this.options.ai
318
- };
318
+ const userConfig = mergeAIConfigObjects(globalConfig.ai || {}, this.options.ai);
319
319
  const aiConfig = loadEnvConfig(userConfig, {
320
320
  packageName: "ai",
321
321
  prefix: "SMRT",
@@ -329,6 +329,30 @@ var SmrtClass = class SmrtClass {
329
329
  maxTokens: "number"
330
330
  }
331
331
  });
332
+ const resolvedAi = tryResolveAIProviderConfig({
333
+ explicit: userConfig,
334
+ config: aiConfigBlock,
335
+ prefixes: ["SMRT_AI"],
336
+ autoDetect: false,
337
+ requireProvider: false,
338
+ providerKeyEnvFallback: Boolean(aiConfigBlock)
339
+ });
340
+ const bound = {
341
+ provider: resolvedAi?.provider,
342
+ apiKey: resolvedAi?.apiKey,
343
+ baseUrl: resolvedAi?.baseUrl,
344
+ model: resolvedAi?.model
345
+ };
346
+ for (const key of [
347
+ "provider",
348
+ "type",
349
+ "apiKey",
350
+ "baseUrl",
351
+ "model",
352
+ "defaultModel"
353
+ ]) delete aiConfig[key];
354
+ for (const [key, value] of Object.entries(bound)) if (value) aiConfig[key] = value;
355
+ Object.assign(aiConfig, withAIAliases(aiConfig));
332
356
  const existingOnUsage = aiConfig.onUsage ?? userConfig.onUsage ?? void 0;
333
357
  aiConfig.onUsage = async (event) => {
334
358
  if (typeof existingOnUsage === "function") await existingOnUsage(event);
package/dist/class.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"class.js","names":[],"sources":["../src/class.ts"],"sourcesContent":["import type { AIClient, AIClientOptions } from '@happyvertical/ai';\nimport type {\n FilesystemAdapter,\n FilesystemAdapterOptions,\n} from '@happyvertical/files';\nimport { createLogger, type LoggerConfig } from '@happyvertical/logger';\nimport type {\n AiTokenUsage,\n AiUsageHandler,\n AiUsageListOptions,\n AiUsageSnapshot,\n AiUsageStats,\n AiUsageSummaryOptions,\n SignalAdapter,\n SmrtAiUsageEvent,\n SmrtAiUsageRecord,\n} from '@happyvertical/smrt-types';\nimport { type DatabaseInterface, getDatabase } from '@happyvertical/sql';\nimport {\n AiUsageCollector,\n AiUsagePersistenceHandler,\n} from './adapters/ai-usage.js';\nimport { estimateAiUsageCost } from './adapters/cost-rates.js';\nimport { registerChangeFeedWriter } from './change-feed.js';\nimport {\n getSmrtModuleConfig,\n type SmrtGlobalConfig,\n} from './config/global-config.js';\nimport type {\n AIConfig,\n AiUsageConfig,\n GlobalSignalConfig,\n MetricsConfig,\n PubSubConfig,\n} from './config.js';\nimport { config } from './config.js';\nimport type { DatabaseConfig } from './database.js';\nimport {\n type DecisionClient,\n type DecisionConfig,\n type DecisionOptions,\n type DecisionRequest,\n type DecisionResult,\n executeDecision,\n} from './decisions.js';\nimport { createFilesystemAdapter } from './filesystem-loader.js';\nimport { applyPostgresRuntimeTimeouts } from './postgres-timeouts.js';\nimport { detectEngine } from './schema/ddl/index.js';\nimport { SignalBus } from './signals/bus.js';\nimport { ensureSystemTables as ensureFrameworkSystemTables } from './system/bootstrap.js';\nimport {\n ensureDeferredSystemTableCompatibility,\n tableExists,\n} from './system/compatibility.js';\nimport { SMRT_SCHEMA_VERSION } from './system/schema.js';\nimport { toSafeInteger } from './utils/safe-integer.js';\n\n/**\n * `_smrt_migrations.version` suffix marking that the deferred (manifest-created)\n * system tables have been through their compatibility pass (issue #2376).\n */\nconst DEFERRED_COMPATIBILITY_VERSION_SUFFIX = '+deferred-compat';\n\nconst logger = createLogger({ level: 'info' });\n\ntype DatabaseWithConfig = DatabaseInterface & {\n config?: {\n type?: string;\n url?: string;\n };\n type?: string;\n};\n\ninterface ResolvedAiUsageConfig {\n enabled: boolean;\n persist: boolean;\n estimateCosts: boolean;\n costRates?: Record<string, { input: number; output: number }>;\n handlers: AiUsageHandler[];\n}\n\ntype AiUsageFilterOptions = Pick<\n AiUsageListOptions,\n | 'since'\n | 'until'\n | 'provider'\n | 'model'\n | 'operation'\n | 'className'\n | 'tenantId'\n>;\n\ninterface AiUsageWhereClause {\n conditions: string[];\n params: unknown[];\n nextParamIndex: number;\n}\n\nfunction firstString(...candidates: unknown[]): string | undefined {\n return candidates.find((candidate): candidate is string => {\n return typeof candidate === 'string';\n });\n}\n\nfunction firstNumber(...candidates: unknown[]): number | undefined {\n return candidates.find((candidate): candidate is number => {\n return typeof candidate === 'number';\n });\n}\n\nfunction getDatabaseUrl(db: DatabaseInterface): string {\n const dbWithConfig = db as DatabaseWithConfig;\n return db.url || dbWithConfig.config?.url || '';\n}\n\nfunction getDatabaseTypeHint(\n config: DatabaseConfig | undefined,\n): string | undefined {\n if (!config || typeof config === 'string') {\n return undefined;\n }\n\n const configWithType = config as DatabaseWithConfig & {\n client?: unknown;\n };\n\n if (typeof configWithType.type === 'string') {\n return configWithType.type;\n }\n\n if (typeof configWithType.config?.type === 'string') {\n return configWithType.config.type;\n }\n\n if ('query' in configWithType && typeof configWithType.query === 'function') {\n return undefined;\n }\n\n if ('client' in configWithType && configWithType.client) {\n return 'postgres';\n }\n\n return undefined;\n}\n\nfunction normalizeIncomingAiUsageTokens(\n value: unknown,\n): AiTokenUsage | undefined {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n\n const usage = value as Record<string, unknown>;\n const promptTokens =\n typeof usage.promptTokens === 'number'\n ? usage.promptTokens\n : typeof usage.inputTokens === 'number'\n ? usage.inputTokens\n : undefined;\n const completionTokens =\n typeof usage.completionTokens === 'number'\n ? usage.completionTokens\n : typeof usage.outputTokens === 'number'\n ? usage.outputTokens\n : undefined;\n const totalTokens =\n typeof usage.totalTokens === 'number'\n ? usage.totalTokens\n : promptTokens !== undefined || completionTokens !== undefined\n ? (promptTokens ?? 0) + (completionTokens ?? 0)\n : undefined;\n\n if (\n promptTokens === undefined &&\n completionTokens === undefined &&\n totalTokens === undefined\n ) {\n return undefined;\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens,\n };\n}\n\nfunction hydratePersistedAiUsageTokens(row: {\n prompt_tokens?: unknown;\n completion_tokens?: unknown;\n total_tokens?: unknown;\n}): AiTokenUsage | undefined {\n const promptTokens =\n row.prompt_tokens === null || row.prompt_tokens === undefined\n ? undefined\n : toSafeInteger(row.prompt_tokens, 'AI usage prompt tokens');\n const completionTokens =\n row.completion_tokens === null || row.completion_tokens === undefined\n ? undefined\n : toSafeInteger(row.completion_tokens, 'AI usage completion tokens');\n const totalTokens =\n row.total_tokens === null || row.total_tokens === undefined\n ? undefined\n : toSafeInteger(row.total_tokens, 'AI usage total tokens');\n\n if (\n promptTokens === undefined &&\n completionTokens === undefined &&\n totalTokens === undefined\n ) {\n return undefined;\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens,\n };\n}\n\nfunction normalizeAiUsageTags(\n value: unknown,\n): Record<string, string> | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n const tags: Record<string, string> = {};\n for (const [key, tagValue] of Object.entries(value)) {\n if (tagValue === undefined || tagValue === null) continue;\n tags[key] = String(tagValue);\n }\n\n return Object.keys(tags).length > 0 ? tags : undefined;\n}\n\nfunction parseAiUsageTags(value: unknown): Record<string, string> | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n\n try {\n return normalizeAiUsageTags(JSON.parse(value));\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeAiUsageTimestamp(value: unknown): Date {\n if (value instanceof Date) {\n return value;\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n const date = new Date(value);\n if (!Number.isNaN(date.getTime())) {\n return date;\n }\n }\n\n return new Date();\n}\n\nfunction getQueryRows(\n result: Awaited<ReturnType<DatabaseInterface['query']>>,\n): Record<string, unknown>[] {\n return Array.isArray(result)\n ? (result as Record<string, unknown>[])\n : ((result as { rows?: Record<string, unknown>[] }).rows ?? []);\n}\n\nfunction buildAiUsageWhereClause(\n options: AiUsageFilterOptions,\n): AiUsageWhereClause {\n const conditions: string[] = [];\n const params: unknown[] = [];\n let nextParamIndex = 1;\n\n if (options.since) {\n conditions.push(`created_at >= $${nextParamIndex++}`);\n params.push(options.since.toISOString());\n }\n\n if (options.until) {\n conditions.push(`created_at <= $${nextParamIndex++}`);\n params.push(options.until.toISOString());\n }\n\n if (options.provider) {\n conditions.push(`provider = $${nextParamIndex++}`);\n params.push(options.provider);\n }\n\n if (options.model) {\n conditions.push(`model = $${nextParamIndex++}`);\n params.push(options.model);\n }\n\n if (options.operation) {\n conditions.push(`operation = $${nextParamIndex++}`);\n params.push(options.operation);\n }\n\n if (options.className) {\n conditions.push(`class_name = $${nextParamIndex++}`);\n params.push(options.className);\n }\n\n if (options.tenantId === null) {\n conditions.push(`tenant_id IS NULL`);\n } else if (options.tenantId) {\n conditions.push(`tenant_id = $${nextParamIndex++}`);\n params.push(options.tenantId);\n }\n\n return {\n conditions,\n params,\n nextParamIndex,\n };\n}\n\n/**\n * Configuration options for the SmrtClass\n */\nexport interface SmrtClassOptions {\n /**\n * Optional custom class name override\n */\n _className?: string;\n\n /**\n * Database configuration - unified approach matching @happyvertical/sql\n *\n * Supports three formats:\n * - String shortcut: 'products.db' (auto-detects database type)\n * - Config object: { type: 'sqlite', url: 'products.db' }\n * - DatabaseInterface instance: await getDatabase(...)\n *\n * @see DatabaseConfig for type definition\n */\n db?: DatabaseConfig;\n\n /**\n * Alias for db option - for backward compatibility with documentation\n *\n * @deprecated Use 'db' instead. This alias exists for backward compatibility.\n */\n persistence?: DatabaseConfig;\n\n /**\n * Filesystem adapter configuration options\n */\n fs?: FilesystemAdapterOptions;\n\n /**\n * AI client configuration options or instance\n */\n ai?: AIClientOptions | AIClient;\n\n /**\n * Optional typed-decision provider or already-built decision-capable client.\n * It is independent from `ai`, which remains the generation client.\n */\n decisions?: DecisionConfig | DecisionClient;\n\n /**\n * AI usage tracking configuration (overrides global defaults)\n */\n usage?: AiUsageConfig;\n\n /**\n * Logging configuration (overrides global default)\n */\n logging?: LoggerConfig;\n\n /**\n * Metrics configuration (overrides global default)\n */\n metrics?: MetricsConfig;\n\n /**\n * Pub/Sub configuration (overrides global default)\n */\n pubsub?: PubSubConfig;\n\n /**\n * Sanitization configuration (overrides global default)\n */\n sanitization?: import('./config.js').GlobalSignalConfig['sanitization'];\n\n /**\n * Custom signal configuration (overrides global default)\n */\n signals?: {\n /** Shared signal bus instance */\n bus?: SignalBus;\n /** Additional custom adapters */\n adapters?: SignalAdapter[];\n };\n\n /**\n * Internal flag to reuse an already initialized DatabaseInterface instance.\n *\n * Skips database resolution and system-table setup during lightweight hydration.\n * @internal\n */\n _reuseInitializedDb?: boolean;\n\n /**\n * Internal flag to defer runtime-only services such as signals and AI setup.\n *\n * Lightweight hydration paths set this so plain query reads avoid per-row\n * runtime bootstrap costs.\n * @internal\n */\n _deferRuntimeInitialization?: boolean;\n}\n\n/**\n * Foundation class providing core functionality for the SMRT framework\n *\n * SmrtClass provides unified access to database, filesystem, and AI client\n * interfaces. It serves as the foundation for all other classes in the\n * SMRT framework.\n */\nexport class SmrtClass {\n /**\n * AI client instance for interacting with AI models\n */\n protected _ai!: AIClient;\n\n /** Optional, independently initialized typed-decision client. */\n protected _decisionClient?: DecisionClient;\n\n private _decisionClientInitPromise?: Promise<DecisionClient | undefined>;\n\n /**\n * Filesystem adapter for file operations\n */\n protected _fs!: FilesystemAdapter;\n\n /**\n * Database interface for data persistence\n */\n protected _db!: DatabaseInterface;\n private _dbEngineHint?: string;\n\n /**\n * Class name used for identification\n */\n protected _className!: string;\n\n /**\n * Signal bus for method execution tracking\n */\n protected _signalBus?: SignalBus;\n\n /**\n * Adapters registered by this instance (for cleanup)\n */\n private _registeredAdapters: SignalAdapter[] = [];\n\n /**\n * In-memory AI usage collector for quick inspection.\n */\n private _aiUsageCollector?: AiUsageCollector;\n\n /**\n * Registered AI usage handlers for this instance.\n */\n private _aiUsageHandlers: AiUsageHandler[] = [];\n\n /**\n * Tracks whether optional runtime services (signals, AI, fs) are ready.\n */\n private _runtimeServicesInitialized = false;\n\n /**\n * Shared in-flight runtime initialization promise for single-flight setup.\n */\n private _runtimeServicesInitPromise?: Promise<void>;\n\n /**\n * Configuration options provided to the class\n */\n public options: SmrtClassOptions;\n\n /**\n * Track which databases have had system tables initialized\n * - WeakSet for :memory: databases (URL not unique, track by instance)\n * - Set<string> for all others (URL is unique identifier)\n */\n private static _systemTablesInitialized = new WeakSet<DatabaseInterface>();\n private static _systemTablesInitializedByUrl = new Set<string>();\n\n /**\n * Creates a new SmrtClass instance\n *\n * @param options - Configuration options for database, filesystem, and AI clients\n */\n constructor(options: SmrtClassOptions = {}) {\n this.options = options;\n this._className = this.constructor.name;\n }\n\n /**\n * Determines whether this class requires a database to function\n *\n * Override this method in subclasses that require database access\n * to enable early validation during initialization.\n *\n * @returns True if database is required, false otherwise\n * @example\n * ```typescript\n * class MyDataModel extends SmrtClass {\n * protected requiresDatabase(): boolean {\n * return true; // This class needs database access\n * }\n * }\n * ```\n */\n protected requiresDatabase(): boolean {\n return false; // Base class doesn't require database by default\n }\n\n /**\n * Initializes database, filesystem, and AI client connections\n *\n * This method sets up all required services based on the provided options.\n * It should be called before using any of the service interfaces.\n *\n * @returns Promise that resolves to this instance for chaining\n * @throws {Error} If database is required but not provided in options\n */\n protected async initialize(): Promise<this> {\n await this.initializeCoreResources();\n\n if (!this.options._deferRuntimeInitialization) {\n await this.initializeRuntimeServices();\n }\n\n return this;\n }\n\n /**\n * Initialize core resources required for ORM behavior.\n *\n * This setup is shared by both full runtime initialization and lightweight\n * query hydration. Hydrated objects reuse an existing DB connection and skip\n * repeated system-table checks.\n */\n protected async initializeCoreResources(): Promise<void> {\n // Framework init hook for the change feed (#1758): make sure the writer\n // interceptor observes every save/delete before any instance can write.\n // Idempotent and cheap when already registered.\n registerChangeFeedWriter();\n\n // Map persistence to db for backward compatibility\n if (this.options.persistence && !this.options.db) {\n this.options.db = this.options.persistence;\n }\n\n // Validate database configuration if required\n if (this.requiresDatabase() && !this.options.db) {\n throw new Error(\n `${this._className} requires a database configuration. ` +\n `Please provide 'db' in options: { db: { url: '...' } } or { db: 'database.db' }`,\n );\n }\n\n if (this.options.db) {\n this._dbEngineHint = getDatabaseTypeHint(this.options.db);\n\n if (\n this.options._reuseInitializedDb &&\n typeof this.options.db === 'object' &&\n 'query' in this.options.db\n ) {\n this._db = this.options.db as DatabaseInterface;\n this.options.db = this._db;\n } else {\n // Handle four db config formats (in implementation order):\n // 1. String URL: 'products.db' (shortcut)\n // 2. DatabaseInterface instance: already initialized db (has 'query' method)\n // 3. Config with client: { type: 'postgres', client: pgPool } (SvelteKit pattern)\n // 4. Config object: { type: 'sqlite', url: 'products.db' }\n if (typeof this.options.db === 'string') {\n // Format 1: String shortcut - let getDatabase auto-detect type from URL\n // Preserve connection sharing for file-backed databases while leaving\n // true in-memory databases isolated per instance.\n const isMemoryDb = this.options.db === ':memory:';\n // PostgreSQL URLs pick up the runtime timeout bounds, and the dbid is\n // derived from the bounded URL so the same rewrite at every call site\n // still resolves to one shared pool (#2377).\n const bounded = applyPostgresRuntimeTimeouts({\n url: this.options.db,\n });\n this._db = await getDatabase({\n ...bounded,\n ...(isMemoryDb ? {} : { dbid: `smrt:${bounded.url}` }),\n } as unknown as Parameters<typeof getDatabase>[0]);\n } else if ('query' in this.options.db) {\n // Format 2: Already a DatabaseInterface instance - return as-is\n this._db = this.options.db as DatabaseInterface;\n } else if ('client' in this.options.db && this.options.db.client) {\n // Format 3: Config with pre-created client (e.g., from SvelteKit's $env-based connection)\n // Pass the client to getDatabase which will use it instead of creating a new connection\n const dbConfig = this.options.db as {\n type?: string;\n client: unknown;\n url?: string;\n };\n // `client` is a runtime-only property the postgres adapter reads\n // but the public `getDatabase` option union does not model, so the\n // literal is routed through the function's own parameter type.\n this._db = await getDatabase({\n type: dbConfig.type || 'postgres',\n client: dbConfig.client,\n url: dbConfig.url,\n } as unknown as Parameters<typeof getDatabase>[0]);\n } else {\n // Format 4: Config object - pass to getDatabase (handles all types uniformly)\n // Preserve connection sharing for file-backed databases while leaving\n // true in-memory databases isolated per instance.\n const dbConfig = this.options.db as { url?: string; type?: string };\n const dbUrl = dbConfig.url || 'memory';\n const isMemoryDb = dbUrl === ':memory:' || dbUrl === 'memory';\n // Keep the config's own key set: an object with no `url` must stay\n // that way so the adapter applies its own default (#2377).\n const bounded = applyPostgresRuntimeTimeouts({ ...this.options.db });\n // The loose config-object variant of `DatabaseConfig` carries an\n // index signature that the closed `getDatabase` option union does\n // not accept structurally, so route through its parameter type.\n this._db = await getDatabase({\n ...bounded,\n ...(isMemoryDb ? {} : { dbid: `smrt:${bounded.url ?? dbUrl}` }),\n } as unknown as Parameters<typeof getDatabase>[0]);\n }\n\n /**\n * INTENTIONAL MUTATION: After resolving the database config,\n * we replace options.db with the actual DatabaseInterface instance.\n * This enables child objects to share the same connection via:\n *\n * const child = new ChildObject({ db: parent.options.db });\n *\n * Without this, passing this.options to getCollection() would use the config object\n * which causes a NEW db instance to be created, losing data isolation.\n *\n * See issue #567 for context on why this pattern is necessary.\n */\n this.options.db = this._db;\n\n await this.ensureSystemTables();\n }\n }\n }\n\n /**\n * Initialize optional runtime services that are not required for plain ORM reads.\n */\n protected async initializeRuntimeServices(): Promise<void> {\n if (this._runtimeServicesInitialized) {\n return;\n }\n\n if (!this._runtimeServicesInitPromise) {\n this._runtimeServicesInitPromise = (async () => {\n if (this.options.fs && !this._fs) {\n // Acquired through the boundary in filesystem-loader.ts so the\n // @happyvertical/files SDK (S3/googleapis) never enters\n // provider-neutral consumer bundles (#1977/#1979).\n this._fs = await createFilesystemAdapter(this.options.fs);\n }\n\n // Initialize AI client with environment variable support\n // Priority: instance options > env vars > global config > defaults\n const globalConfig = config.toJSON();\n const usageConfig = this.mergeAiUsageConfig(globalConfig);\n this.initializeAiUsageHandlers(usageConfig);\n\n if (\n !this._ai &&\n (this.options.ai || globalConfig.ai || process.env.SMRT_AI_PROVIDER)\n ) {\n // Check if options.ai is already a client-like object with embed method\n // This allows passing mock AI clients for testing\n const aiOption = this.options.ai as\n | Record<string, unknown>\n | undefined;\n if (\n aiOption &&\n typeof aiOption === 'object' &&\n typeof aiOption.embed === 'function' &&\n !aiOption.provider\n ) {\n this._ai = aiOption as unknown as AIClient;\n } else {\n // CC-8 follow-up: ideally this would route through\n // `@happyvertical/smrt-config` for sanitization parity (e.g. via\n // `getPackageConfig('ai', ...)`), but smrt-config currently only\n // merges file-based config + runtime overrides — it does NOT\n // read from `process.env` with a typed prefix/schema. Until\n // smrt-config grows an env-loader (or wraps `loadEnvConfig`),\n // we continue to use the underlying utility directly. Tracked\n // alongside the CC-8 audit on issue #1199.\n const { loadEnvConfig } = await import('@happyvertical/utils');\n\n // Start with global defaults\n const baseConfig = globalConfig.ai || {};\n\n // Merge with instance options (takes priority over global)\n const userConfig = { ...baseConfig, ...this.options.ai };\n\n // Load environment variables and merge (user options take priority).\n // `AIConfig` carries an index signature, so provider-specific keys\n // (`type`, `onUsage`, `defaultModel`, …) read back as `unknown`.\n const aiConfig = loadEnvConfig<AIConfig>(userConfig, {\n packageName: 'ai',\n prefix: 'SMRT',\n schema: {\n provider: 'string',\n model: 'string',\n apiKey: 'string',\n timeout: 'number',\n maxRetries: 'number',\n temperature: 'number',\n maxTokens: 'number',\n },\n });\n\n const existingOnUsage =\n aiConfig.onUsage ??\n (userConfig as Record<string, unknown>).onUsage ??\n undefined;\n aiConfig.onUsage = async (event: unknown) => {\n if (typeof existingOnUsage === 'function') {\n await (existingOnUsage as (usageEvent: unknown) => unknown)(\n event,\n );\n }\n await this.handleAiUsageCallback(event, aiConfig, usageConfig);\n };\n\n // Only initialize if we have a provider configured\n if (aiConfig.provider || aiConfig.type || aiConfig.apiKey) {\n // Use the getAI() factory (loaded lazily to keep the\n // `@happyvertical/ai` module graph off smrt-core's cold-start\n // import path — see issue #2894) to support all AI providers\n // (OpenAI, Anthropic, Gemini, etc.). getAI() returns\n // AIInterface, which we narrow to AIClient for backward\n // compatibility. The index-signature `AIConfig` is routed\n // through getAI's own parameter type rather than the closed\n // union.\n const { getAI } = await import('@happyvertical/ai');\n this._ai = (await getAI(\n aiConfig as unknown as Parameters<typeof getAI>[0],\n )) as unknown as AIClient;\n }\n }\n }\n\n await this.initializeSignals();\n this._runtimeServicesInitialized = true;\n })();\n }\n\n try {\n await this._runtimeServicesInitPromise;\n } finally {\n this._runtimeServicesInitPromise = undefined;\n }\n }\n\n /**\n * Ensure deferred runtime services are ready before using them.\n */\n protected async ensureRuntimeServicesInitialized(): Promise<void> {\n if (!this._runtimeServicesInitialized) {\n await this.initializeRuntimeServices();\n }\n }\n\n /**\n * Resolve the AI client, initializing deferred runtime services on demand.\n */\n protected async getAiClient(): Promise<AIClient> {\n await this.ensureRuntimeServicesInitialized();\n\n if (!this._ai) {\n throw new Error(\n `${this._className} does not have an AI client configured. ` +\n `Provide 'ai' in options or configure a global SMRT AI provider.`,\n );\n }\n\n return this._ai;\n }\n\n /**\n * Resolve the AI client if one is configured, otherwise return undefined.\n */\n protected async getOptionalAiClient(): Promise<AIClient | undefined> {\n await this.ensureRuntimeServicesInitialized();\n return this._ai;\n }\n\n /**\n * Resolve the optional typed-decision client without changing the generation\n * client used by `is()`, `do()`, or `describe()`. The SDK import stays inside\n * this opt-in path so ordinary SMRT applications neither load nor configure\n * the decision provider.\n */\n protected async getDecisionClient(): Promise<DecisionClient | undefined> {\n await this.ensureRuntimeServicesInitialized();\n\n if (this._decisionClient) return this._decisionClient;\n if (this._decisionClientInitPromise) {\n return this._decisionClientInitPromise;\n }\n\n this._decisionClientInitPromise = (async () => {\n const instanceDecision = this.options.decisions;\n if (\n instanceDecision &&\n typeof instanceDecision === 'object' &&\n typeof (instanceDecision as DecisionClient).decide === 'function'\n ) {\n this._decisionClient = instanceDecision as DecisionClient;\n return this._decisionClient;\n }\n\n const configuredDecision = this.getDecisionConfig();\n if (!configuredDecision) return undefined;\n if (configuredDecision.type !== 'typesafe') {\n throw new Error(\"Decision configuration requires type: 'typesafe'.\");\n }\n\n // `getAI()` is deliberately dynamically imported, matching the existing\n // generation setup. The #1284 SDK adds `decide` as an optional interface\n // capability, so third-party/older clients remain source-compatible.\n const { getAI } = await import('@happyvertical/ai');\n this._decisionClient = (await getAI(\n configuredDecision as Parameters<typeof getAI>[0],\n )) as unknown as DecisionClient;\n return this._decisionClient;\n })();\n\n try {\n return await this._decisionClientInitPromise;\n } finally {\n this._decisionClientInitPromise = undefined;\n }\n }\n\n /**\n * Attempt one typed decision batch against caller-curated state.\n *\n * `undefined` is the explicit legacy-route signal: no decision client was\n * configured. Configured capability, provider, and response errors always\n * propagate. Object methods with registered tools must retain their\n * generation route and therefore do not call this helper.\n */\n protected async attemptDecision(\n request: DecisionRequest,\n options?: DecisionOptions,\n ): Promise<DecisionResult | undefined> {\n const decision = await this.getDecisionClient();\n if (!decision) return undefined;\n\n const result = await executeDecision(decision, request, options);\n await this.recordAiUsageEvent(\n {\n provider: result.provenance.provider,\n model: result.provenance.model,\n operation: 'decision',\n usage: result.usage,\n tags: { operation: 'decision' },\n },\n result.provenance,\n );\n return result;\n }\n\n /** Resolve project, global, and instance decision options in that order. */\n protected getDecisionConfig(): DecisionConfig | undefined {\n const instanceDecision = this.options.decisions;\n const instanceConfig =\n instanceDecision &&\n typeof instanceDecision === 'object' &&\n typeof (instanceDecision as DecisionClient).decide !== 'function'\n ? (instanceDecision as Partial<DecisionConfig>)\n : undefined;\n const projectDecision = getSmrtModuleConfig<SmrtGlobalConfig>('smrt', {})\n .decisions as Partial<DecisionConfig> | undefined;\n const globalDecision = config.toJSON().decisions as\n | Partial<DecisionConfig>\n | undefined;\n const resolved = {\n ...projectDecision,\n ...globalDecision,\n ...instanceConfig,\n } as Partial<DecisionConfig>;\n return Object.keys(resolved).length === 0\n ? undefined\n : (resolved as DecisionConfig);\n }\n\n /**\n * Records an SDK operation through SMRT's established usage handlers. The\n * event carries provider/model/tokens only; prompts, object data, and secrets\n * never enter usage persistence or callbacks.\n */\n protected async recordAiUsageEvent(\n event: unknown,\n providerConfig: Record<string, unknown>,\n ): Promise<void> {\n await this.ensureRuntimeServicesInitialized();\n await this.handleAiUsageCallback(\n event,\n providerConfig,\n this.mergeAiUsageConfig(config.toJSON()),\n );\n }\n\n private async isSystemSchemaVersionApplied(\n db: DatabaseInterface,\n version: string,\n ): Promise<boolean> {\n const engine = detectEngine(getDatabaseUrl(db), this._dbEngineHint);\n\n if (\n engine === 'postgres' &&\n !(await tableExists(db, '_smrt_migrations', this._dbEngineHint))\n ) {\n return false;\n }\n\n try {\n const versionParam = engine === 'postgres' ? '$1' : '?';\n const rows = await db.query(\n `SELECT 1 FROM _smrt_migrations WHERE version = ${versionParam} LIMIT 1`,\n version,\n );\n return getQueryRows(rows).length > 0;\n } catch (error) {\n if (engine === 'postgres') {\n throw error;\n }\n\n return false;\n }\n }\n\n /**\n * Ensure SMRT system tables exist in the database\n *\n * System tables use the _smrt_ prefix and store framework metadata:\n * - _smrt_contexts: Context memory storage for remembered patterns\n * - _smrt_migrations: Schema version tracking\n * - _smrt_schema_migrations / _smrt_backfills: Migration and backfill ledgers\n * - _smrt_embeddings: Embedding vectors for semantic search\n * - _smrt_dispatch / _smrt_dispatch_subscriptions: Inter-agent dispatch queue\n * - _smrt_ai_usage: AI usage telemetry\n * - _smrt_changes: Append-only change feed\n *\n * Note that the _smrt_ prefix alone does NOT mean \"system table\" — ~25\n * `@smrt()` domain tables (feature flags, prompt overrides, subscription\n * plans, …) carry it too and are created by `db:migrate`, not here. The\n * framework's own list is `SYSTEM_TABLE_NAMES` in\n * `schema/system-table-shapes.ts`, derived from this DDL.\n *\n * This method is idempotent and safe to call multiple times.\n * Tables are only created once per database connection.\n */\n private async ensureSystemTables(): Promise<void> {\n if (!this._db) return;\n\n const dbUrl = getDatabaseUrl(this._db);\n\n // Some databases share URLs but are different instances:\n // - :memory: databases (SQLite/DuckDB in-memory)\n // - JSON databases (may have undefined or shared URLs)\n // - Any database with undefined URL\n // Use WeakSet for instance tracking, Set<string> for URL tracking\n const dbConstructorName = this._db.constructor?.name || '';\n const isMemoryDb = dbUrl === ':memory:';\n const isJsonDb = dbConstructorName.toLowerCase().includes('json');\n const hasUndefinedUrl = !dbUrl;\n const useInstanceTracking = isMemoryDb || isJsonDb || hasUndefinedUrl;\n\n if (useInstanceTracking) {\n // Check WeakSet for databases that may share URLs (track by instance)\n if (SmrtClass._systemTablesInitialized.has(this._db)) {\n return;\n }\n } else {\n // Check Set<string> for URL-based databases (track by URL)\n if (SmrtClass._systemTablesInitializedByUrl.has(dbUrl)) {\n return;\n }\n }\n\n try {\n await ensureFrameworkSystemTables(this._db, this._dbEngineHint);\n await this.settleDeferredSystemTableCompatibility(this._db);\n await this.ensureNativeVectorStorage();\n\n // Mark as initialized using appropriate tracking mechanism\n if (useInstanceTracking) {\n SmrtClass._systemTablesInitialized.add(this._db);\n } else {\n SmrtClass._systemTablesInitializedByUrl.add(dbUrl);\n }\n } catch (error) {\n // DO NOT SWALLOW ERRORS - fail loudly so we know what's wrong\n const dbInfo = this._db.constructor?.name || 'unknown database';\n throw new Error(\n `Failed to create system tables for ${dbInfo}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n }\n\n /**\n * Run the compatibility pass for the system tables `db:migrate` creates, then\n * stamp its own marker in `_smrt_migrations` (issue #2376).\n *\n * Deliberately runs OUTSIDE the bootstrap lock/transaction, and deliberately\n * cannot fail startup:\n *\n * - `_smrt_jobs` / `_smrt_job_events` are created by `db:migrate` from the\n * jobs manifest, which on a fresh install runs AFTER the framework's first\n * bootstrap. Gating this pass on {@link SMRT_SCHEMA_VERSION} would mean the\n * version gets stamped while the tables are still absent and the pass never\n * runs on that database again. It therefore carries its own marker, which\n * is written only once every deferred table exists and has been upgraded.\n * - Its statements are ALTER TABLE / CREATE INDEX against tables the\n * framework does not own. Inside the PostgreSQL bootstrap transaction one\n * failure (a permissions problem, say) would abort the transaction and roll\n * back system-table creation with it. Outside, a failure is a logged\n * warning and the next process start retries — the pass is idempotent and\n * already tolerates concurrent creators.\n *\n * Until the tables exist this costs two catalog probes per process per\n * database; afterwards the marker short-circuits it.\n */\n private async settleDeferredSystemTableCompatibility(\n db: DatabaseInterface,\n ): Promise<void> {\n const marker = `${SMRT_SCHEMA_VERSION}${DEFERRED_COMPATIBILITY_VERSION_SUFFIX}`;\n\n try {\n if (await this.isSystemSchemaVersionApplied(db, marker)) {\n return;\n }\n\n const { settled } = await ensureDeferredSystemTableCompatibility(\n db,\n this._dbEngineHint,\n );\n if (!settled) {\n return;\n }\n\n const id = crypto.randomUUID();\n const description = 'Deferred SMRT system table compatibility';\n await db.execute`\n INSERT INTO _smrt_migrations (id, version, description)\n VALUES (${id}, ${marker}, ${description})\n ON CONFLICT(version) DO NOTHING\n `;\n } catch (error) {\n logger.warn(\n `[smrt] Deferred system table compatibility did not complete; retrying on the next start: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n private async ensureNativeVectorStorage(): Promise<void> {\n if (!this._db) {\n return;\n }\n\n try {\n const { ObjectRegistry } = await import('./registry.js');\n const embeddingConfig = ObjectRegistry.getProjectEmbeddingConfig();\n if (embeddingConfig?.storage === 'native') {\n const { EmbeddingStorage } = await import('./embeddings/storage.js');\n const vector = this._db.vector;\n if (vector) {\n const dimensions = embeddingConfig.dimensions || 768;\n await EmbeddingStorage.ensureVectorStorage(\n this._db,\n dimensions,\n vector,\n );\n } else {\n logger.warn(\n '[smrt] Embedding storage set to \"native\" but database has no vector capability. Falling back to JSON storage.',\n );\n }\n }\n } catch (error) {\n // Don't fail system table initialization for vector setup errors\n logger.warn(\n `[smrt] Failed to initialize vector storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n /**\n * Access system tables through standard database interface\n * System tables use _smrt_ prefix to avoid conflicts with user tables\n */\n protected get systemDb(): DatabaseInterface {\n return this._db;\n }\n\n /**\n * Return the database engine hint captured from the caller's configuration.\n * Adapters may not expose their original type after construction (notably\n * in-memory DuckDB/JSON connections), so query helpers can combine this\n * hint with their public connection capabilities.\n */\n protected getDatabaseEngineHint(): string | undefined {\n return this._dbEngineHint;\n }\n\n /**\n * Initialize signal bus and adapters\n *\n * Merges global configuration with instance-specific overrides.\n * Registers built-in and custom adapters based on configuration.\n */\n private async initializeSignals(): Promise<void> {\n const globalConfig = config.toJSON();\n const effectiveConfig = this.mergeSignalConfig(globalConfig);\n\n // If a shared bus is provided, always use it (don't create new adapters)\n if (this.options.signals?.bus) {\n this._signalBus = this.options.signals.bus;\n return;\n }\n\n // Otherwise, check if we should initialize signals based on config\n if (!this.shouldInitializeSignals(effectiveConfig)) {\n return;\n }\n\n this._signalBus = new SignalBus({\n sanitization: effectiveConfig.sanitization,\n });\n await this.registerAdapters(effectiveConfig);\n }\n\n /**\n * Merge global and instance signal configuration\n *\n * Instance configuration takes priority over global defaults.\n *\n * @param globalConfig - Global configuration from smrt.configure()\n * @returns Merged configuration\n */\n private mergeSignalConfig(\n globalConfig: GlobalSignalConfig,\n ): GlobalSignalConfig {\n return {\n logging: this.options.logging ?? globalConfig.logging,\n metrics: this.options.metrics ?? globalConfig.metrics,\n pubsub: this.options.pubsub ?? globalConfig.pubsub,\n usage: this.mergeAiUsageConfig(globalConfig),\n sanitization: this.options.sanitization ?? globalConfig.sanitization,\n signals: {\n bus: this.options.signals?.bus ?? globalConfig.signals?.bus,\n adapters: [\n ...(globalConfig.signals?.adapters ?? []),\n ...(this.options.signals?.adapters ?? []),\n ],\n },\n };\n }\n\n /**\n * Check if signals should be initialized\n *\n * Signals are initialized if any adapter is configured.\n *\n * @param config - Effective signal configuration\n * @returns True if signals should be initialized\n */\n private shouldInitializeSignals(config: GlobalSignalConfig): boolean {\n return !!(\n config.logging !== false ||\n config.metrics?.enabled ||\n config.pubsub?.enabled ||\n config.signals?.adapters?.length\n );\n }\n\n /**\n * Register signal adapters based on configuration\n *\n * @param config - Effective signal configuration\n */\n private async registerAdapters(config: GlobalSignalConfig): Promise<void> {\n if (!this._signalBus) return;\n\n // Logging adapter (default: enabled with console)\n if (config.logging !== false) {\n const { createLogger, LoggerAdapter } = await import(\n '@happyvertical/logger'\n );\n const logger = createLogger(config.logging ?? true);\n const adapter = new LoggerAdapter(logger);\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Metrics adapter (default: disabled)\n if (config.metrics?.enabled) {\n const { MetricsAdapter } = await import('./adapters/metrics.js');\n const adapter = new MetricsAdapter();\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Pub/Sub adapter (default: disabled)\n if (config.pubsub?.enabled) {\n const { PubSubAdapter } = await import('./adapters/pubsub.js');\n const adapter = new PubSubAdapter();\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Custom adapters\n if (config.signals?.adapters) {\n for (const adapter of config.signals.adapters) {\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n }\n }\n\n /**\n * Gets the filesystem adapter instance\n */\n get fs() {\n return this._fs;\n }\n\n /**\n * Gets the database interface instance\n */\n get db() {\n // Throw helpful error if database is accessed before initialization\n if (!this._db) {\n throw new Error(\n `Database accessed before initialization. ` +\n `Please call await instance.initialize() before accessing the database.`,\n );\n }\n return this._db;\n }\n\n /**\n * Temporarily bind this initialized instance to another database interface.\n *\n * This is intended for transaction-scoped work where a caller must ensure\n * that every nested SMRT read and write uses the transaction database passed\n * to its callback. The original database is restored whether the callback\n * succeeds or throws. Do not use the same instance concurrently while it is\n * temporarily bound.\n */\n async withDatabase<T>(\n db: DatabaseInterface,\n operation: (bound: this) => Promise<T>,\n ): Promise<T> {\n // Read through the public getter so an uninitialized instance fails with\n // the normal, actionable SmrtClass error before any state is mutated.\n const previousDb = this.db;\n const previousOptionDb = this.options.db;\n this._db = db;\n this.options.db = db;\n try {\n return await operation(this);\n } finally {\n this._db = previousDb;\n this.options.db = previousOptionDb;\n }\n }\n\n /**\n * Gets the AI client instance\n */\n get ai() {\n return this._ai;\n }\n\n /**\n * Get the in-memory AI usage snapshot for this instance.\n */\n getAiUsageSnapshot(): AiUsageSnapshot | undefined {\n return this._aiUsageCollector?.getSnapshot();\n }\n\n /**\n * Reset the in-memory AI usage collector.\n */\n resetAiUsage(): void {\n this._aiUsageCollector?.reset();\n }\n\n /**\n * List persisted AI usage records.\n */\n async listAiUsage(\n options: AiUsageListOptions = {},\n ): Promise<SmrtAiUsageRecord[]> {\n if (!this._db) {\n throw new Error(\n `AI usage requires a database configuration. ` +\n `Please call initialize() with a db option before querying usage.`,\n );\n }\n\n const { conditions, params } = buildAiUsageWhereClause(options);\n let paramIndex = params.length + 1;\n\n let sql = 'SELECT * FROM _smrt_ai_usage';\n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`;\n }\n\n sql += ` ORDER BY ${\n options.orderBy === 'timestamp ASC' ? 'created_at ASC' : 'created_at DESC'\n }`;\n\n if (options.limit !== undefined) {\n sql += ` LIMIT $${paramIndex++}`;\n params.push(options.limit);\n }\n\n if (options.offset !== undefined) {\n sql += ` OFFSET $${paramIndex++}`;\n params.push(options.offset);\n }\n\n const rows = getQueryRows(await this._db.query(sql, ...params));\n\n return rows.map((row) => ({\n id: String(row.id),\n provider: String(row.provider),\n model: String(row.model),\n operation: String(row.operation),\n usage: hydratePersistedAiUsageTokens(row),\n estimatedCost:\n row.estimated_cost === null || row.estimated_cost === undefined\n ? undefined\n : Number(row.estimated_cost),\n duration: toSafeInteger(row.duration ?? 0, 'AI usage duration'),\n className:\n row.class_name === null || row.class_name === undefined\n ? undefined\n : String(row.class_name),\n tenantId:\n row.tenant_id === undefined\n ? undefined\n : (row.tenant_id as string | null),\n tags: parseAiUsageTags(row.tags),\n timestamp: normalizeAiUsageTimestamp(row.created_at),\n }));\n }\n\n /**\n * Summarize persisted AI usage records by a grouping dimension.\n */\n async summarizeAiUsage(\n options: AiUsageSummaryOptions = {},\n ): Promise<Record<string, AiUsageStats>> {\n if (!this._db) {\n throw new Error(\n `AI usage requires a database configuration. ` +\n `Please call initialize() with a db option before querying usage.`,\n );\n }\n\n const groupBy = options.groupBy ?? 'model';\n const bucketExpression =\n groupBy === 'provider'\n ? `provider`\n : groupBy === 'model'\n ? `provider || ':' || model`\n : groupBy === 'class'\n ? `COALESCE(class_name, 'unknown')`\n : groupBy === 'tenant'\n ? `COALESCE(tenant_id, 'global')`\n : groupBy === 'operation'\n ? `operation`\n : `substr(CAST(created_at AS TEXT), 1, 10)`;\n\n const { conditions, params } = buildAiUsageWhereClause(options);\n\n let sql = `\n SELECT ${bucketExpression} AS bucket,\n COUNT(*) AS call_count,\n COALESCE(SUM(prompt_tokens), 0) AS prompt_tokens,\n COALESCE(SUM(completion_tokens), 0) AS completion_tokens,\n COALESCE(SUM(total_tokens), 0) AS total_tokens,\n COALESCE(SUM(duration), 0) AS total_duration,\n COALESCE(SUM(estimated_cost), 0) AS estimated_cost,\n MAX(created_at) AS last_used\n FROM _smrt_ai_usage\n `;\n\n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`;\n }\n\n sql += ' GROUP BY bucket ORDER BY bucket ASC';\n\n const rows = getQueryRows(await this._db.query(sql, ...params));\n const summary: Record<string, AiUsageStats> = {};\n\n for (const row of rows) {\n const bucket = String(row.bucket);\n summary[bucket] = {\n callCount: toSafeInteger(row.call_count ?? 0, 'AI usage call count'),\n promptTokens: toSafeInteger(\n row.prompt_tokens ?? 0,\n 'AI usage prompt-token total',\n ),\n completionTokens: toSafeInteger(\n row.completion_tokens ?? 0,\n 'AI usage completion-token total',\n ),\n totalTokens: toSafeInteger(\n row.total_tokens ?? 0,\n 'AI usage token total',\n ),\n totalDuration: toSafeInteger(\n row.total_duration ?? 0,\n 'AI usage duration total',\n ),\n estimatedCost: Number(row.estimated_cost ?? 0),\n lastUsed: row.last_used ? new Date(String(row.last_used)).getTime() : 0,\n };\n }\n\n return summary;\n }\n\n /**\n * Gets the signal bus instance\n *\n * @returns Signal bus if signals are enabled, undefined otherwise\n */\n get signalBus(): SignalBus | undefined {\n return this._signalBus;\n }\n\n /**\n * Cleanup method to prevent memory leaks\n *\n * Unregisters all adapters from the signal bus that were registered\n * by this instance. Call this when the SmrtClass instance is no longer\n * needed to prevent memory leaks.\n *\n * @example\n * ```typescript\n * const product = new Product({ name: 'Widget' });\n * await product.initialize();\n * // ... use product ...\n * product.destroy(); // Clean up when done\n * ```\n */\n destroy(): void {\n // Only unregister adapters if we own the bus (not shared)\n if (this._signalBus && !this.options.signals?.bus) {\n for (const adapter of this._registeredAdapters) {\n this._signalBus.unregister(adapter);\n }\n this._registeredAdapters = [];\n }\n\n // TODO: If SmrtClass grows a broader async teardown lifecycle, move\n // AI usage handler cleanup there alongside other connection-bound state.\n this._aiUsageCollector = undefined;\n this._aiUsageHandlers = [];\n }\n\n private mergeAiUsageConfig(\n globalConfig: GlobalSignalConfig,\n ): ResolvedAiUsageConfig {\n const globalUsage = globalConfig.usage ?? {};\n const instanceUsage = this.options.usage ?? {};\n\n return {\n enabled: instanceUsage.enabled ?? globalUsage.enabled ?? true,\n persist: instanceUsage.persist ?? globalUsage.persist ?? true,\n estimateCosts:\n instanceUsage.estimateCosts ?? globalUsage.estimateCosts ?? true,\n costRates: {\n ...(globalUsage.costRates ?? {}),\n ...(instanceUsage.costRates ?? {}),\n },\n handlers: [\n ...(globalUsage.handlers ?? []),\n ...(instanceUsage.handlers ?? []),\n ],\n };\n }\n\n private initializeAiUsageHandlers(config: ResolvedAiUsageConfig): void {\n this._aiUsageCollector = undefined;\n this._aiUsageHandlers = [];\n\n if (!config.enabled) {\n return;\n }\n\n this._aiUsageCollector = new AiUsageCollector();\n this._aiUsageHandlers.push(this._aiUsageCollector);\n\n if (config.persist && this._db) {\n this._aiUsageHandlers.push(new AiUsagePersistenceHandler(this._db));\n }\n\n this._aiUsageHandlers.push(...config.handlers);\n }\n\n private async handleAiUsageCallback(\n event: unknown,\n aiConfig: Record<string, unknown>,\n usageConfig: ResolvedAiUsageConfig,\n ): Promise<void> {\n if (!usageConfig.enabled || this._aiUsageHandlers.length === 0) {\n return;\n }\n\n const normalizedEvent = this.normalizeAiUsageEvent(event, aiConfig);\n if (!normalizedEvent) {\n return;\n }\n\n if (usageConfig.estimateCosts) {\n normalizedEvent.estimatedCost = estimateAiUsageCost(\n normalizedEvent.provider,\n normalizedEvent.model,\n normalizedEvent.usage,\n usageConfig.costRates,\n );\n }\n\n const results = await Promise.allSettled(\n this._aiUsageHandlers.map((handler) => handler.handle(normalizedEvent)),\n );\n\n for (const result of results) {\n if (result.status === 'rejected') {\n logger.warn(\n `[smrt] AI usage handler failed for ${normalizedEvent.provider}:${normalizedEvent.model}: ${\n result.reason instanceof Error\n ? result.reason.message\n : String(result.reason)\n }`,\n );\n }\n }\n }\n\n private normalizeAiUsageEvent(\n event: unknown,\n aiConfig: Record<string, unknown>,\n ): SmrtAiUsageEvent | undefined {\n const raw = (event ?? {}) as Record<string, unknown>;\n const provider = firstString(\n raw.provider,\n raw.type,\n aiConfig.provider,\n aiConfig.type,\n );\n const model = firstString(\n raw.model,\n raw.defaultModel,\n aiConfig.model,\n aiConfig.defaultModel,\n );\n const operation =\n firstString(raw.operation, raw.kind, raw.method) ?? 'unknown';\n const usage =\n normalizeIncomingAiUsageTokens(raw.usage) ??\n normalizeIncomingAiUsageTokens(raw.tokenUsage) ??\n normalizeIncomingAiUsageTokens({\n promptTokens: raw.promptTokens,\n completionTokens: raw.completionTokens,\n totalTokens: raw.totalTokens,\n });\n\n if (!provider || !model) {\n return undefined;\n }\n\n const duration =\n firstNumber(raw.duration, raw.durationMs, raw.latency) ?? 0;\n const tenantId =\n 'tenantId' in this &&\n (this as { tenantId?: string | null }).tenantId !== undefined\n ? ((this as { tenantId?: string | null }).tenantId ?? null)\n : undefined;\n\n return {\n provider,\n model,\n operation,\n usage,\n duration,\n timestamp: normalizeAiUsageTimestamp(raw.timestamp),\n tags: normalizeAiUsageTags(raw.tags),\n className: this._className,\n tenantId,\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"class.js","names":[],"sources":["../src/class.ts"],"sourcesContent":["import type { AIClient, AIClientOptions } from '@happyvertical/ai';\nimport type {\n FilesystemAdapter,\n FilesystemAdapterOptions,\n} from '@happyvertical/files';\nimport { createLogger, type LoggerConfig } from '@happyvertical/logger';\nimport {\n type AIExplicitConfig,\n getAIConfigBlock,\n mergeAIConfigObjects,\n tryResolveAIProviderConfig,\n withAIAliases,\n} from '@happyvertical/smrt-config';\nimport type {\n AiTokenUsage,\n AiUsageHandler,\n AiUsageListOptions,\n AiUsageSnapshot,\n AiUsageStats,\n AiUsageSummaryOptions,\n SignalAdapter,\n SmrtAiUsageEvent,\n SmrtAiUsageRecord,\n} from '@happyvertical/smrt-types';\nimport { type DatabaseInterface, getDatabase } from '@happyvertical/sql';\nimport {\n AiUsageCollector,\n AiUsagePersistenceHandler,\n} from './adapters/ai-usage.js';\nimport { estimateAiUsageCost } from './adapters/cost-rates.js';\nimport { registerChangeFeedWriter } from './change-feed.js';\nimport {\n getSmrtModuleConfig,\n type SmrtGlobalConfig,\n} from './config/global-config.js';\nimport type {\n AIConfig,\n AiUsageConfig,\n GlobalSignalConfig,\n MetricsConfig,\n PubSubConfig,\n} from './config.js';\nimport { config } from './config.js';\nimport type { DatabaseConfig } from './database.js';\nimport {\n type DecisionClient,\n type DecisionConfig,\n type DecisionOptions,\n type DecisionRequest,\n type DecisionResult,\n executeDecision,\n} from './decisions.js';\nimport { createFilesystemAdapter } from './filesystem-loader.js';\nimport { applyPostgresRuntimeTimeouts } from './postgres-timeouts.js';\nimport { detectEngine } from './schema/ddl/index.js';\nimport { SignalBus } from './signals/bus.js';\nimport { ensureSystemTables as ensureFrameworkSystemTables } from './system/bootstrap.js';\nimport {\n ensureDeferredSystemTableCompatibility,\n tableExists,\n} from './system/compatibility.js';\nimport { SMRT_SCHEMA_VERSION } from './system/schema.js';\nimport { toSafeInteger } from './utils/safe-integer.js';\n\n/**\n * `_smrt_migrations.version` suffix marking that the deferred (manifest-created)\n * system tables have been through their compatibility pass (issue #2376).\n */\nconst DEFERRED_COMPATIBILITY_VERSION_SUFFIX = '+deferred-compat';\n\nconst logger = createLogger({ level: 'info' });\n\ntype DatabaseWithConfig = DatabaseInterface & {\n config?: {\n type?: string;\n url?: string;\n };\n type?: string;\n};\n\ninterface ResolvedAiUsageConfig {\n enabled: boolean;\n persist: boolean;\n estimateCosts: boolean;\n costRates?: Record<string, { input: number; output: number }>;\n handlers: AiUsageHandler[];\n}\n\ntype AiUsageFilterOptions = Pick<\n AiUsageListOptions,\n | 'since'\n | 'until'\n | 'provider'\n | 'model'\n | 'operation'\n | 'className'\n | 'tenantId'\n>;\n\ninterface AiUsageWhereClause {\n conditions: string[];\n params: unknown[];\n nextParamIndex: number;\n}\n\nfunction firstString(...candidates: unknown[]): string | undefined {\n return candidates.find((candidate): candidate is string => {\n return typeof candidate === 'string';\n });\n}\n\nfunction firstNumber(...candidates: unknown[]): number | undefined {\n return candidates.find((candidate): candidate is number => {\n return typeof candidate === 'number';\n });\n}\n\nfunction getDatabaseUrl(db: DatabaseInterface): string {\n const dbWithConfig = db as DatabaseWithConfig;\n return db.url || dbWithConfig.config?.url || '';\n}\n\nfunction getDatabaseTypeHint(\n config: DatabaseConfig | undefined,\n): string | undefined {\n if (!config || typeof config === 'string') {\n return undefined;\n }\n\n const configWithType = config as DatabaseWithConfig & {\n client?: unknown;\n };\n\n if (typeof configWithType.type === 'string') {\n return configWithType.type;\n }\n\n if (typeof configWithType.config?.type === 'string') {\n return configWithType.config.type;\n }\n\n if ('query' in configWithType && typeof configWithType.query === 'function') {\n return undefined;\n }\n\n if ('client' in configWithType && configWithType.client) {\n return 'postgres';\n }\n\n return undefined;\n}\n\nfunction normalizeIncomingAiUsageTokens(\n value: unknown,\n): AiTokenUsage | undefined {\n if (!value || typeof value !== 'object') {\n return undefined;\n }\n\n const usage = value as Record<string, unknown>;\n const promptTokens =\n typeof usage.promptTokens === 'number'\n ? usage.promptTokens\n : typeof usage.inputTokens === 'number'\n ? usage.inputTokens\n : undefined;\n const completionTokens =\n typeof usage.completionTokens === 'number'\n ? usage.completionTokens\n : typeof usage.outputTokens === 'number'\n ? usage.outputTokens\n : undefined;\n const totalTokens =\n typeof usage.totalTokens === 'number'\n ? usage.totalTokens\n : promptTokens !== undefined || completionTokens !== undefined\n ? (promptTokens ?? 0) + (completionTokens ?? 0)\n : undefined;\n\n if (\n promptTokens === undefined &&\n completionTokens === undefined &&\n totalTokens === undefined\n ) {\n return undefined;\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens,\n };\n}\n\nfunction hydratePersistedAiUsageTokens(row: {\n prompt_tokens?: unknown;\n completion_tokens?: unknown;\n total_tokens?: unknown;\n}): AiTokenUsage | undefined {\n const promptTokens =\n row.prompt_tokens === null || row.prompt_tokens === undefined\n ? undefined\n : toSafeInteger(row.prompt_tokens, 'AI usage prompt tokens');\n const completionTokens =\n row.completion_tokens === null || row.completion_tokens === undefined\n ? undefined\n : toSafeInteger(row.completion_tokens, 'AI usage completion tokens');\n const totalTokens =\n row.total_tokens === null || row.total_tokens === undefined\n ? undefined\n : toSafeInteger(row.total_tokens, 'AI usage total tokens');\n\n if (\n promptTokens === undefined &&\n completionTokens === undefined &&\n totalTokens === undefined\n ) {\n return undefined;\n }\n\n return {\n promptTokens,\n completionTokens,\n totalTokens,\n };\n}\n\nfunction normalizeAiUsageTags(\n value: unknown,\n): Record<string, string> | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n const tags: Record<string, string> = {};\n for (const [key, tagValue] of Object.entries(value)) {\n if (tagValue === undefined || tagValue === null) continue;\n tags[key] = String(tagValue);\n }\n\n return Object.keys(tags).length > 0 ? tags : undefined;\n}\n\nfunction parseAiUsageTags(value: unknown): Record<string, string> | undefined {\n if (typeof value !== 'string') {\n return undefined;\n }\n\n try {\n return normalizeAiUsageTags(JSON.parse(value));\n } catch {\n return undefined;\n }\n}\n\nfunction normalizeAiUsageTimestamp(value: unknown): Date {\n if (value instanceof Date) {\n return value;\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n const date = new Date(value);\n if (!Number.isNaN(date.getTime())) {\n return date;\n }\n }\n\n return new Date();\n}\n\nfunction getQueryRows(\n result: Awaited<ReturnType<DatabaseInterface['query']>>,\n): Record<string, unknown>[] {\n return Array.isArray(result)\n ? (result as Record<string, unknown>[])\n : ((result as { rows?: Record<string, unknown>[] }).rows ?? []);\n}\n\nfunction buildAiUsageWhereClause(\n options: AiUsageFilterOptions,\n): AiUsageWhereClause {\n const conditions: string[] = [];\n const params: unknown[] = [];\n let nextParamIndex = 1;\n\n if (options.since) {\n conditions.push(`created_at >= $${nextParamIndex++}`);\n params.push(options.since.toISOString());\n }\n\n if (options.until) {\n conditions.push(`created_at <= $${nextParamIndex++}`);\n params.push(options.until.toISOString());\n }\n\n if (options.provider) {\n conditions.push(`provider = $${nextParamIndex++}`);\n params.push(options.provider);\n }\n\n if (options.model) {\n conditions.push(`model = $${nextParamIndex++}`);\n params.push(options.model);\n }\n\n if (options.operation) {\n conditions.push(`operation = $${nextParamIndex++}`);\n params.push(options.operation);\n }\n\n if (options.className) {\n conditions.push(`class_name = $${nextParamIndex++}`);\n params.push(options.className);\n }\n\n if (options.tenantId === null) {\n conditions.push(`tenant_id IS NULL`);\n } else if (options.tenantId) {\n conditions.push(`tenant_id = $${nextParamIndex++}`);\n params.push(options.tenantId);\n }\n\n return {\n conditions,\n params,\n nextParamIndex,\n };\n}\n\n/**\n * Configuration options for the SmrtClass\n */\nexport interface SmrtClassOptions {\n /**\n * Optional custom class name override\n */\n _className?: string;\n\n /**\n * Database configuration - unified approach matching @happyvertical/sql\n *\n * Supports three formats:\n * - String shortcut: 'products.db' (auto-detects database type)\n * - Config object: { type: 'sqlite', url: 'products.db' }\n * - DatabaseInterface instance: await getDatabase(...)\n *\n * @see DatabaseConfig for type definition\n */\n db?: DatabaseConfig;\n\n /**\n * Alias for db option - for backward compatibility with documentation\n *\n * @deprecated Use 'db' instead. This alias exists for backward compatibility.\n */\n persistence?: DatabaseConfig;\n\n /**\n * Filesystem adapter configuration options\n */\n fs?: FilesystemAdapterOptions;\n\n /**\n * AI client configuration options or instance\n */\n ai?: AIClientOptions | AIClient;\n\n /**\n * Optional typed-decision provider or already-built decision-capable client.\n * It is independent from `ai`, which remains the generation client.\n */\n decisions?: DecisionConfig | DecisionClient;\n\n /**\n * AI usage tracking configuration (overrides global defaults)\n */\n usage?: AiUsageConfig;\n\n /**\n * Logging configuration (overrides global default)\n */\n logging?: LoggerConfig;\n\n /**\n * Metrics configuration (overrides global default)\n */\n metrics?: MetricsConfig;\n\n /**\n * Pub/Sub configuration (overrides global default)\n */\n pubsub?: PubSubConfig;\n\n /**\n * Sanitization configuration (overrides global default)\n */\n sanitization?: import('./config.js').GlobalSignalConfig['sanitization'];\n\n /**\n * Custom signal configuration (overrides global default)\n */\n signals?: {\n /** Shared signal bus instance */\n bus?: SignalBus;\n /** Additional custom adapters */\n adapters?: SignalAdapter[];\n };\n\n /**\n * Internal flag to reuse an already initialized DatabaseInterface instance.\n *\n * Skips database resolution and system-table setup during lightweight hydration.\n * @internal\n */\n _reuseInitializedDb?: boolean;\n\n /**\n * Internal flag to defer runtime-only services such as signals and AI setup.\n *\n * Lightweight hydration paths set this so plain query reads avoid per-row\n * runtime bootstrap costs.\n * @internal\n */\n _deferRuntimeInitialization?: boolean;\n}\n\n/**\n * Foundation class providing core functionality for the SMRT framework\n *\n * SmrtClass provides unified access to database, filesystem, and AI client\n * interfaces. It serves as the foundation for all other classes in the\n * SMRT framework.\n */\nexport class SmrtClass {\n /**\n * AI client instance for interacting with AI models\n */\n protected _ai!: AIClient;\n\n /** Optional, independently initialized typed-decision client. */\n protected _decisionClient?: DecisionClient;\n\n private _decisionClientInitPromise?: Promise<DecisionClient | undefined>;\n\n /**\n * Filesystem adapter for file operations\n */\n protected _fs!: FilesystemAdapter;\n\n /**\n * Database interface for data persistence\n */\n protected _db!: DatabaseInterface;\n private _dbEngineHint?: string;\n\n /**\n * Class name used for identification\n */\n protected _className!: string;\n\n /**\n * Signal bus for method execution tracking\n */\n protected _signalBus?: SignalBus;\n\n /**\n * Adapters registered by this instance (for cleanup)\n */\n private _registeredAdapters: SignalAdapter[] = [];\n\n /**\n * In-memory AI usage collector for quick inspection.\n */\n private _aiUsageCollector?: AiUsageCollector;\n\n /**\n * Registered AI usage handlers for this instance.\n */\n private _aiUsageHandlers: AiUsageHandler[] = [];\n\n /**\n * Tracks whether optional runtime services (signals, AI, fs) are ready.\n */\n private _runtimeServicesInitialized = false;\n\n /**\n * Shared in-flight runtime initialization promise for single-flight setup.\n */\n private _runtimeServicesInitPromise?: Promise<void>;\n\n /**\n * Configuration options provided to the class\n */\n public options: SmrtClassOptions;\n\n /**\n * Track which databases have had system tables initialized\n * - WeakSet for :memory: databases (URL not unique, track by instance)\n * - Set<string> for all others (URL is unique identifier)\n */\n private static _systemTablesInitialized = new WeakSet<DatabaseInterface>();\n private static _systemTablesInitializedByUrl = new Set<string>();\n\n /**\n * Creates a new SmrtClass instance\n *\n * @param options - Configuration options for database, filesystem, and AI clients\n */\n constructor(options: SmrtClassOptions = {}) {\n this.options = options;\n this._className = this.constructor.name;\n }\n\n /**\n * Determines whether this class requires a database to function\n *\n * Override this method in subclasses that require database access\n * to enable early validation during initialization.\n *\n * @returns True if database is required, false otherwise\n * @example\n * ```typescript\n * class MyDataModel extends SmrtClass {\n * protected requiresDatabase(): boolean {\n * return true; // This class needs database access\n * }\n * }\n * ```\n */\n protected requiresDatabase(): boolean {\n return false; // Base class doesn't require database by default\n }\n\n /**\n * Initializes database, filesystem, and AI client connections\n *\n * This method sets up all required services based on the provided options.\n * It should be called before using any of the service interfaces.\n *\n * @returns Promise that resolves to this instance for chaining\n * @throws {Error} If database is required but not provided in options\n */\n protected async initialize(): Promise<this> {\n await this.initializeCoreResources();\n\n if (!this.options._deferRuntimeInitialization) {\n await this.initializeRuntimeServices();\n }\n\n return this;\n }\n\n /**\n * Initialize core resources required for ORM behavior.\n *\n * This setup is shared by both full runtime initialization and lightweight\n * query hydration. Hydrated objects reuse an existing DB connection and skip\n * repeated system-table checks.\n */\n protected async initializeCoreResources(): Promise<void> {\n // Framework init hook for the change feed (#1758): make sure the writer\n // interceptor observes every save/delete before any instance can write.\n // Idempotent and cheap when already registered.\n registerChangeFeedWriter();\n\n // Map persistence to db for backward compatibility\n if (this.options.persistence && !this.options.db) {\n this.options.db = this.options.persistence;\n }\n\n // Validate database configuration if required\n if (this.requiresDatabase() && !this.options.db) {\n throw new Error(\n `${this._className} requires a database configuration. ` +\n `Please provide 'db' in options: { db: { url: '...' } } or { db: 'database.db' }`,\n );\n }\n\n if (this.options.db) {\n this._dbEngineHint = getDatabaseTypeHint(this.options.db);\n\n if (\n this.options._reuseInitializedDb &&\n typeof this.options.db === 'object' &&\n 'query' in this.options.db\n ) {\n this._db = this.options.db as DatabaseInterface;\n this.options.db = this._db;\n } else {\n // Handle four db config formats (in implementation order):\n // 1. String URL: 'products.db' (shortcut)\n // 2. DatabaseInterface instance: already initialized db (has 'query' method)\n // 3. Config with client: { type: 'postgres', client: pgPool } (SvelteKit pattern)\n // 4. Config object: { type: 'sqlite', url: 'products.db' }\n if (typeof this.options.db === 'string') {\n // Format 1: String shortcut - let getDatabase auto-detect type from URL\n // Preserve connection sharing for file-backed databases while leaving\n // true in-memory databases isolated per instance.\n const isMemoryDb = this.options.db === ':memory:';\n // PostgreSQL URLs pick up the runtime timeout bounds, and the dbid is\n // derived from the bounded URL so the same rewrite at every call site\n // still resolves to one shared pool (#2377).\n const bounded = applyPostgresRuntimeTimeouts({\n url: this.options.db,\n });\n this._db = await getDatabase({\n ...bounded,\n ...(isMemoryDb ? {} : { dbid: `smrt:${bounded.url}` }),\n } as unknown as Parameters<typeof getDatabase>[0]);\n } else if ('query' in this.options.db) {\n // Format 2: Already a DatabaseInterface instance - return as-is\n this._db = this.options.db as DatabaseInterface;\n } else if ('client' in this.options.db && this.options.db.client) {\n // Format 3: Config with pre-created client (e.g., from SvelteKit's $env-based connection)\n // Pass the client to getDatabase which will use it instead of creating a new connection\n const dbConfig = this.options.db as {\n type?: string;\n client: unknown;\n url?: string;\n };\n // `client` is a runtime-only property the postgres adapter reads\n // but the public `getDatabase` option union does not model, so the\n // literal is routed through the function's own parameter type.\n this._db = await getDatabase({\n type: dbConfig.type || 'postgres',\n client: dbConfig.client,\n url: dbConfig.url,\n } as unknown as Parameters<typeof getDatabase>[0]);\n } else {\n // Format 4: Config object - pass to getDatabase (handles all types uniformly)\n // Preserve connection sharing for file-backed databases while leaving\n // true in-memory databases isolated per instance.\n const dbConfig = this.options.db as { url?: string; type?: string };\n const dbUrl = dbConfig.url || 'memory';\n const isMemoryDb = dbUrl === ':memory:' || dbUrl === 'memory';\n // Keep the config's own key set: an object with no `url` must stay\n // that way so the adapter applies its own default (#2377).\n const bounded = applyPostgresRuntimeTimeouts({ ...this.options.db });\n // The loose config-object variant of `DatabaseConfig` carries an\n // index signature that the closed `getDatabase` option union does\n // not accept structurally, so route through its parameter type.\n this._db = await getDatabase({\n ...bounded,\n ...(isMemoryDb ? {} : { dbid: `smrt:${bounded.url ?? dbUrl}` }),\n } as unknown as Parameters<typeof getDatabase>[0]);\n }\n\n /**\n * INTENTIONAL MUTATION: After resolving the database config,\n * we replace options.db with the actual DatabaseInterface instance.\n * This enables child objects to share the same connection via:\n *\n * const child = new ChildObject({ db: parent.options.db });\n *\n * Without this, passing this.options to getCollection() would use the config object\n * which causes a NEW db instance to be created, losing data isolation.\n *\n * See issue #567 for context on why this pattern is necessary.\n */\n this.options.db = this._db;\n\n await this.ensureSystemTables();\n }\n }\n }\n\n /**\n * Initialize optional runtime services that are not required for plain ORM reads.\n */\n protected async initializeRuntimeServices(): Promise<void> {\n if (this._runtimeServicesInitialized) {\n return;\n }\n\n if (!this._runtimeServicesInitPromise) {\n this._runtimeServicesInitPromise = (async () => {\n if (this.options.fs && !this._fs) {\n // Acquired through the boundary in filesystem-loader.ts so the\n // @happyvertical/files SDK (S3/googleapis) never enters\n // provider-neutral consumer bundles (#1977/#1979).\n this._fs = await createFilesystemAdapter(this.options.fs);\n }\n\n // Initialize AI client with environment variable support\n // Priority: instance options > env vars > global config > defaults\n const globalConfig = config.toJSON();\n const usageConfig = this.mergeAiUsageConfig(globalConfig);\n this.initializeAiUsageHandlers(usageConfig);\n\n // The smrt.config.ts `ai` block (resolved through smrt-config's shared\n // resolver) is the lowest-priority declared source, above SMRT_AI_*.\n // Only a block that names a provider counts: a partial block (model,\n // baseUrl or apiKeyEnv alone) behaves exactly like an absent block.\n const declaredBlock = getAIConfigBlock();\n const aiConfigBlock = declaredBlock?.provider ? declaredBlock : null;\n if (\n !this._ai &&\n (this.options.ai ||\n globalConfig.ai ||\n aiConfigBlock ||\n process.env.SMRT_AI_PROVIDER)\n ) {\n // Check if options.ai is already a client-like object with embed method\n // This allows passing mock AI clients for testing\n const aiOption = this.options.ai as\n | Record<string, unknown>\n | undefined;\n if (\n aiOption &&\n typeof aiOption === 'object' &&\n typeof aiOption.embed === 'function' &&\n !aiOption.provider\n ) {\n this._ai = aiOption as unknown as AIClient;\n } else {\n // Passthrough tuning fields (timeout, maxRetries, ...) still come\n // from `SMRT_AI_*` via loadEnvConfig. Provider/key/model selection\n // goes through the shared smrt-config resolver below (#3372).\n const { loadEnvConfig } = await import('@happyvertical/utils');\n\n // Start with global defaults\n const baseConfig = globalConfig.ai || {};\n\n // Merge with instance options (takes priority over global)\n // Provider ownership holds across these two layers too: the shared\n // canonicalising merge folds provider/type and model/defaultModel,\n // and drops the global layer's provider, key, base URL and model\n // when the instance options name a different provider.\n const userConfig = mergeAIConfigObjects(\n baseConfig,\n this.options.ai,\n );\n\n // Load environment variables and merge (user options take priority).\n // `AIConfig` carries an index signature, so provider-specific keys\n // (`type`, `onUsage`, `defaultModel`, …) read back as `unknown`.\n const aiConfig = loadEnvConfig<AIConfig>(userConfig, {\n packageName: 'ai',\n prefix: 'SMRT',\n schema: {\n provider: 'string',\n model: 'string',\n apiKey: 'string',\n timeout: 'number',\n maxRetries: 'number',\n temperature: 'number',\n maxTokens: 'number',\n },\n });\n\n // Provider, key, base URL and model always come from the shared\n // resolver's bound result (options.ai > core global config >\n // smrt.config `ai` block > SMRT_AI_*), so a credential is only ever\n // sent to the provider its own source named; env-loaded values are\n // replaced, not merged. Tuning fields keep their current source.\n // Without a block the provider's own key variable is not consulted\n // (no behaviour change for apps that never declared one).\n const resolvedAi = tryResolveAIProviderConfig({\n explicit: userConfig as AIExplicitConfig,\n config: aiConfigBlock,\n prefixes: ['SMRT_AI'],\n autoDetect: false,\n requireProvider: false,\n providerKeyEnvFallback: Boolean(aiConfigBlock),\n });\n // Replace (never merge) the identity/credential fields with the\n // resolver's bound result, then set both client-facing aliases so\n // no stale `type` / `defaultModel` reaches getAI().\n const bound: Record<string, unknown> = {\n provider: resolvedAi?.provider,\n apiKey: resolvedAi?.apiKey,\n baseUrl: resolvedAi?.baseUrl,\n model: resolvedAi?.model,\n };\n for (const key of [\n 'provider',\n 'type',\n 'apiKey',\n 'baseUrl',\n 'model',\n 'defaultModel',\n ]) {\n delete (aiConfig as Record<string, unknown>)[key];\n }\n for (const [key, value] of Object.entries(bound)) {\n if (value) (aiConfig as Record<string, unknown>)[key] = value;\n }\n Object.assign(\n aiConfig,\n withAIAliases(aiConfig as Record<string, unknown>),\n );\n\n const existingOnUsage =\n aiConfig.onUsage ??\n (userConfig as Record<string, unknown>).onUsage ??\n undefined;\n aiConfig.onUsage = async (event: unknown) => {\n if (typeof existingOnUsage === 'function') {\n await (existingOnUsage as (usageEvent: unknown) => unknown)(\n event,\n );\n }\n await this.handleAiUsageCallback(event, aiConfig, usageConfig);\n };\n\n // Only initialize if we have a provider configured\n if (aiConfig.provider || aiConfig.type || aiConfig.apiKey) {\n // Use the getAI() factory (loaded lazily to keep the\n // `@happyvertical/ai` module graph off smrt-core's cold-start\n // import path — see issue #2894) to support all AI providers\n // (OpenAI, Anthropic, Gemini, etc.). getAI() returns\n // AIInterface, which we narrow to AIClient for backward\n // compatibility. The index-signature `AIConfig` is routed\n // through getAI's own parameter type rather than the closed\n // union.\n const { getAI } = await import('@happyvertical/ai');\n this._ai = (await getAI(\n aiConfig as unknown as Parameters<typeof getAI>[0],\n )) as unknown as AIClient;\n }\n }\n }\n\n await this.initializeSignals();\n this._runtimeServicesInitialized = true;\n })();\n }\n\n try {\n await this._runtimeServicesInitPromise;\n } finally {\n this._runtimeServicesInitPromise = undefined;\n }\n }\n\n /**\n * Ensure deferred runtime services are ready before using them.\n */\n protected async ensureRuntimeServicesInitialized(): Promise<void> {\n if (!this._runtimeServicesInitialized) {\n await this.initializeRuntimeServices();\n }\n }\n\n /**\n * Resolve the AI client, initializing deferred runtime services on demand.\n */\n protected async getAiClient(): Promise<AIClient> {\n await this.ensureRuntimeServicesInitialized();\n\n if (!this._ai) {\n throw new Error(\n `${this._className} does not have an AI client configured. ` +\n `Provide 'ai' in options or configure a global SMRT AI provider.`,\n );\n }\n\n return this._ai;\n }\n\n /**\n * Resolve the AI client if one is configured, otherwise return undefined.\n */\n protected async getOptionalAiClient(): Promise<AIClient | undefined> {\n await this.ensureRuntimeServicesInitialized();\n return this._ai;\n }\n\n /**\n * Resolve the optional typed-decision client without changing the generation\n * client used by `is()`, `do()`, or `describe()`. The SDK import stays inside\n * this opt-in path so ordinary SMRT applications neither load nor configure\n * the decision provider.\n */\n protected async getDecisionClient(): Promise<DecisionClient | undefined> {\n await this.ensureRuntimeServicesInitialized();\n\n if (this._decisionClient) return this._decisionClient;\n if (this._decisionClientInitPromise) {\n return this._decisionClientInitPromise;\n }\n\n this._decisionClientInitPromise = (async () => {\n const instanceDecision = this.options.decisions;\n if (\n instanceDecision &&\n typeof instanceDecision === 'object' &&\n typeof (instanceDecision as DecisionClient).decide === 'function'\n ) {\n this._decisionClient = instanceDecision as DecisionClient;\n return this._decisionClient;\n }\n\n const configuredDecision = this.getDecisionConfig();\n if (!configuredDecision) return undefined;\n if (configuredDecision.type !== 'typesafe') {\n throw new Error(\"Decision configuration requires type: 'typesafe'.\");\n }\n\n // `getAI()` is deliberately dynamically imported, matching the existing\n // generation setup. The #1284 SDK adds `decide` as an optional interface\n // capability, so third-party/older clients remain source-compatible.\n const { getAI } = await import('@happyvertical/ai');\n this._decisionClient = (await getAI(\n configuredDecision as Parameters<typeof getAI>[0],\n )) as unknown as DecisionClient;\n return this._decisionClient;\n })();\n\n try {\n return await this._decisionClientInitPromise;\n } finally {\n this._decisionClientInitPromise = undefined;\n }\n }\n\n /**\n * Attempt one typed decision batch against caller-curated state.\n *\n * `undefined` is the explicit legacy-route signal: no decision client was\n * configured. Configured capability, provider, and response errors always\n * propagate. Object methods with registered tools must retain their\n * generation route and therefore do not call this helper.\n */\n protected async attemptDecision(\n request: DecisionRequest,\n options?: DecisionOptions,\n ): Promise<DecisionResult | undefined> {\n const decision = await this.getDecisionClient();\n if (!decision) return undefined;\n\n const result = await executeDecision(decision, request, options);\n await this.recordAiUsageEvent(\n {\n provider: result.provenance.provider,\n model: result.provenance.model,\n operation: 'decision',\n usage: result.usage,\n tags: { operation: 'decision' },\n },\n result.provenance,\n );\n return result;\n }\n\n /** Resolve project, global, and instance decision options in that order. */\n protected getDecisionConfig(): DecisionConfig | undefined {\n const instanceDecision = this.options.decisions;\n const instanceConfig =\n instanceDecision &&\n typeof instanceDecision === 'object' &&\n typeof (instanceDecision as DecisionClient).decide !== 'function'\n ? (instanceDecision as Partial<DecisionConfig>)\n : undefined;\n const projectDecision = getSmrtModuleConfig<SmrtGlobalConfig>('smrt', {})\n .decisions as Partial<DecisionConfig> | undefined;\n const globalDecision = config.toJSON().decisions as\n | Partial<DecisionConfig>\n | undefined;\n const resolved = {\n ...projectDecision,\n ...globalDecision,\n ...instanceConfig,\n } as Partial<DecisionConfig>;\n return Object.keys(resolved).length === 0\n ? undefined\n : (resolved as DecisionConfig);\n }\n\n /**\n * Records an SDK operation through SMRT's established usage handlers. The\n * event carries provider/model/tokens only; prompts, object data, and secrets\n * never enter usage persistence or callbacks.\n */\n protected async recordAiUsageEvent(\n event: unknown,\n providerConfig: Record<string, unknown>,\n ): Promise<void> {\n await this.ensureRuntimeServicesInitialized();\n await this.handleAiUsageCallback(\n event,\n providerConfig,\n this.mergeAiUsageConfig(config.toJSON()),\n );\n }\n\n private async isSystemSchemaVersionApplied(\n db: DatabaseInterface,\n version: string,\n ): Promise<boolean> {\n const engine = detectEngine(getDatabaseUrl(db), this._dbEngineHint);\n\n if (\n engine === 'postgres' &&\n !(await tableExists(db, '_smrt_migrations', this._dbEngineHint))\n ) {\n return false;\n }\n\n try {\n const versionParam = engine === 'postgres' ? '$1' : '?';\n const rows = await db.query(\n `SELECT 1 FROM _smrt_migrations WHERE version = ${versionParam} LIMIT 1`,\n version,\n );\n return getQueryRows(rows).length > 0;\n } catch (error) {\n if (engine === 'postgres') {\n throw error;\n }\n\n return false;\n }\n }\n\n /**\n * Ensure SMRT system tables exist in the database\n *\n * System tables use the _smrt_ prefix and store framework metadata:\n * - _smrt_contexts: Context memory storage for remembered patterns\n * - _smrt_migrations: Schema version tracking\n * - _smrt_schema_migrations / _smrt_backfills: Migration and backfill ledgers\n * - _smrt_embeddings: Embedding vectors for semantic search\n * - _smrt_dispatch / _smrt_dispatch_subscriptions: Inter-agent dispatch queue\n * - _smrt_ai_usage: AI usage telemetry\n * - _smrt_changes: Append-only change feed\n *\n * Note that the _smrt_ prefix alone does NOT mean \"system table\" — ~25\n * `@smrt()` domain tables (feature flags, prompt overrides, subscription\n * plans, …) carry it too and are created by `db:migrate`, not here. The\n * framework's own list is `SYSTEM_TABLE_NAMES` in\n * `schema/system-table-shapes.ts`, derived from this DDL.\n *\n * This method is idempotent and safe to call multiple times.\n * Tables are only created once per database connection.\n */\n private async ensureSystemTables(): Promise<void> {\n if (!this._db) return;\n\n const dbUrl = getDatabaseUrl(this._db);\n\n // Some databases share URLs but are different instances:\n // - :memory: databases (SQLite/DuckDB in-memory)\n // - JSON databases (may have undefined or shared URLs)\n // - Any database with undefined URL\n // Use WeakSet for instance tracking, Set<string> for URL tracking\n const dbConstructorName = this._db.constructor?.name || '';\n const isMemoryDb = dbUrl === ':memory:';\n const isJsonDb = dbConstructorName.toLowerCase().includes('json');\n const hasUndefinedUrl = !dbUrl;\n const useInstanceTracking = isMemoryDb || isJsonDb || hasUndefinedUrl;\n\n if (useInstanceTracking) {\n // Check WeakSet for databases that may share URLs (track by instance)\n if (SmrtClass._systemTablesInitialized.has(this._db)) {\n return;\n }\n } else {\n // Check Set<string> for URL-based databases (track by URL)\n if (SmrtClass._systemTablesInitializedByUrl.has(dbUrl)) {\n return;\n }\n }\n\n try {\n await ensureFrameworkSystemTables(this._db, this._dbEngineHint);\n await this.settleDeferredSystemTableCompatibility(this._db);\n await this.ensureNativeVectorStorage();\n\n // Mark as initialized using appropriate tracking mechanism\n if (useInstanceTracking) {\n SmrtClass._systemTablesInitialized.add(this._db);\n } else {\n SmrtClass._systemTablesInitializedByUrl.add(dbUrl);\n }\n } catch (error) {\n // DO NOT SWALLOW ERRORS - fail loudly so we know what's wrong\n const dbInfo = this._db.constructor?.name || 'unknown database';\n throw new Error(\n `Failed to create system tables for ${dbInfo}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n }\n\n /**\n * Run the compatibility pass for the system tables `db:migrate` creates, then\n * stamp its own marker in `_smrt_migrations` (issue #2376).\n *\n * Deliberately runs OUTSIDE the bootstrap lock/transaction, and deliberately\n * cannot fail startup:\n *\n * - `_smrt_jobs` / `_smrt_job_events` are created by `db:migrate` from the\n * jobs manifest, which on a fresh install runs AFTER the framework's first\n * bootstrap. Gating this pass on {@link SMRT_SCHEMA_VERSION} would mean the\n * version gets stamped while the tables are still absent and the pass never\n * runs on that database again. It therefore carries its own marker, which\n * is written only once every deferred table exists and has been upgraded.\n * - Its statements are ALTER TABLE / CREATE INDEX against tables the\n * framework does not own. Inside the PostgreSQL bootstrap transaction one\n * failure (a permissions problem, say) would abort the transaction and roll\n * back system-table creation with it. Outside, a failure is a logged\n * warning and the next process start retries — the pass is idempotent and\n * already tolerates concurrent creators.\n *\n * Until the tables exist this costs two catalog probes per process per\n * database; afterwards the marker short-circuits it.\n */\n private async settleDeferredSystemTableCompatibility(\n db: DatabaseInterface,\n ): Promise<void> {\n const marker = `${SMRT_SCHEMA_VERSION}${DEFERRED_COMPATIBILITY_VERSION_SUFFIX}`;\n\n try {\n if (await this.isSystemSchemaVersionApplied(db, marker)) {\n return;\n }\n\n const { settled } = await ensureDeferredSystemTableCompatibility(\n db,\n this._dbEngineHint,\n );\n if (!settled) {\n return;\n }\n\n const id = crypto.randomUUID();\n const description = 'Deferred SMRT system table compatibility';\n await db.execute`\n INSERT INTO _smrt_migrations (id, version, description)\n VALUES (${id}, ${marker}, ${description})\n ON CONFLICT(version) DO NOTHING\n `;\n } catch (error) {\n logger.warn(\n `[smrt] Deferred system table compatibility did not complete; retrying on the next start: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n private async ensureNativeVectorStorage(): Promise<void> {\n if (!this._db) {\n return;\n }\n\n try {\n const { ObjectRegistry } = await import('./registry.js');\n const embeddingConfig = ObjectRegistry.getProjectEmbeddingConfig();\n if (embeddingConfig?.storage === 'native') {\n const { EmbeddingStorage } = await import('./embeddings/storage.js');\n const vector = this._db.vector;\n if (vector) {\n const dimensions = embeddingConfig.dimensions || 768;\n await EmbeddingStorage.ensureVectorStorage(\n this._db,\n dimensions,\n vector,\n );\n } else {\n logger.warn(\n '[smrt] Embedding storage set to \"native\" but database has no vector capability. Falling back to JSON storage.',\n );\n }\n }\n } catch (error) {\n // Don't fail system table initialization for vector setup errors\n logger.warn(\n `[smrt] Failed to initialize vector storage: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n /**\n * Access system tables through standard database interface\n * System tables use _smrt_ prefix to avoid conflicts with user tables\n */\n protected get systemDb(): DatabaseInterface {\n return this._db;\n }\n\n /**\n * Return the database engine hint captured from the caller's configuration.\n * Adapters may not expose their original type after construction (notably\n * in-memory DuckDB/JSON connections), so query helpers can combine this\n * hint with their public connection capabilities.\n */\n protected getDatabaseEngineHint(): string | undefined {\n return this._dbEngineHint;\n }\n\n /**\n * Initialize signal bus and adapters\n *\n * Merges global configuration with instance-specific overrides.\n * Registers built-in and custom adapters based on configuration.\n */\n private async initializeSignals(): Promise<void> {\n const globalConfig = config.toJSON();\n const effectiveConfig = this.mergeSignalConfig(globalConfig);\n\n // If a shared bus is provided, always use it (don't create new adapters)\n if (this.options.signals?.bus) {\n this._signalBus = this.options.signals.bus;\n return;\n }\n\n // Otherwise, check if we should initialize signals based on config\n if (!this.shouldInitializeSignals(effectiveConfig)) {\n return;\n }\n\n this._signalBus = new SignalBus({\n sanitization: effectiveConfig.sanitization,\n });\n await this.registerAdapters(effectiveConfig);\n }\n\n /**\n * Merge global and instance signal configuration\n *\n * Instance configuration takes priority over global defaults.\n *\n * @param globalConfig - Global configuration from smrt.configure()\n * @returns Merged configuration\n */\n private mergeSignalConfig(\n globalConfig: GlobalSignalConfig,\n ): GlobalSignalConfig {\n return {\n logging: this.options.logging ?? globalConfig.logging,\n metrics: this.options.metrics ?? globalConfig.metrics,\n pubsub: this.options.pubsub ?? globalConfig.pubsub,\n usage: this.mergeAiUsageConfig(globalConfig),\n sanitization: this.options.sanitization ?? globalConfig.sanitization,\n signals: {\n bus: this.options.signals?.bus ?? globalConfig.signals?.bus,\n adapters: [\n ...(globalConfig.signals?.adapters ?? []),\n ...(this.options.signals?.adapters ?? []),\n ],\n },\n };\n }\n\n /**\n * Check if signals should be initialized\n *\n * Signals are initialized if any adapter is configured.\n *\n * @param config - Effective signal configuration\n * @returns True if signals should be initialized\n */\n private shouldInitializeSignals(config: GlobalSignalConfig): boolean {\n return !!(\n config.logging !== false ||\n config.metrics?.enabled ||\n config.pubsub?.enabled ||\n config.signals?.adapters?.length\n );\n }\n\n /**\n * Register signal adapters based on configuration\n *\n * @param config - Effective signal configuration\n */\n private async registerAdapters(config: GlobalSignalConfig): Promise<void> {\n if (!this._signalBus) return;\n\n // Logging adapter (default: enabled with console)\n if (config.logging !== false) {\n const { createLogger, LoggerAdapter } = await import(\n '@happyvertical/logger'\n );\n const logger = createLogger(config.logging ?? true);\n const adapter = new LoggerAdapter(logger);\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Metrics adapter (default: disabled)\n if (config.metrics?.enabled) {\n const { MetricsAdapter } = await import('./adapters/metrics.js');\n const adapter = new MetricsAdapter();\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Pub/Sub adapter (default: disabled)\n if (config.pubsub?.enabled) {\n const { PubSubAdapter } = await import('./adapters/pubsub.js');\n const adapter = new PubSubAdapter();\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n\n // Custom adapters\n if (config.signals?.adapters) {\n for (const adapter of config.signals.adapters) {\n this._signalBus.register(adapter);\n this._registeredAdapters.push(adapter);\n }\n }\n }\n\n /**\n * Gets the filesystem adapter instance\n */\n get fs() {\n return this._fs;\n }\n\n /**\n * Gets the database interface instance\n */\n get db() {\n // Throw helpful error if database is accessed before initialization\n if (!this._db) {\n throw new Error(\n `Database accessed before initialization. ` +\n `Please call await instance.initialize() before accessing the database.`,\n );\n }\n return this._db;\n }\n\n /**\n * Temporarily bind this initialized instance to another database interface.\n *\n * This is intended for transaction-scoped work where a caller must ensure\n * that every nested SMRT read and write uses the transaction database passed\n * to its callback. The original database is restored whether the callback\n * succeeds or throws. Do not use the same instance concurrently while it is\n * temporarily bound.\n */\n async withDatabase<T>(\n db: DatabaseInterface,\n operation: (bound: this) => Promise<T>,\n ): Promise<T> {\n // Read through the public getter so an uninitialized instance fails with\n // the normal, actionable SmrtClass error before any state is mutated.\n const previousDb = this.db;\n const previousOptionDb = this.options.db;\n this._db = db;\n this.options.db = db;\n try {\n return await operation(this);\n } finally {\n this._db = previousDb;\n this.options.db = previousOptionDb;\n }\n }\n\n /**\n * Gets the AI client instance\n */\n get ai() {\n return this._ai;\n }\n\n /**\n * Get the in-memory AI usage snapshot for this instance.\n */\n getAiUsageSnapshot(): AiUsageSnapshot | undefined {\n return this._aiUsageCollector?.getSnapshot();\n }\n\n /**\n * Reset the in-memory AI usage collector.\n */\n resetAiUsage(): void {\n this._aiUsageCollector?.reset();\n }\n\n /**\n * List persisted AI usage records.\n */\n async listAiUsage(\n options: AiUsageListOptions = {},\n ): Promise<SmrtAiUsageRecord[]> {\n if (!this._db) {\n throw new Error(\n `AI usage requires a database configuration. ` +\n `Please call initialize() with a db option before querying usage.`,\n );\n }\n\n const { conditions, params } = buildAiUsageWhereClause(options);\n let paramIndex = params.length + 1;\n\n let sql = 'SELECT * FROM _smrt_ai_usage';\n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`;\n }\n\n sql += ` ORDER BY ${\n options.orderBy === 'timestamp ASC' ? 'created_at ASC' : 'created_at DESC'\n }`;\n\n if (options.limit !== undefined) {\n sql += ` LIMIT $${paramIndex++}`;\n params.push(options.limit);\n }\n\n if (options.offset !== undefined) {\n sql += ` OFFSET $${paramIndex++}`;\n params.push(options.offset);\n }\n\n const rows = getQueryRows(await this._db.query(sql, ...params));\n\n return rows.map((row) => ({\n id: String(row.id),\n provider: String(row.provider),\n model: String(row.model),\n operation: String(row.operation),\n usage: hydratePersistedAiUsageTokens(row),\n estimatedCost:\n row.estimated_cost === null || row.estimated_cost === undefined\n ? undefined\n : Number(row.estimated_cost),\n duration: toSafeInteger(row.duration ?? 0, 'AI usage duration'),\n className:\n row.class_name === null || row.class_name === undefined\n ? undefined\n : String(row.class_name),\n tenantId:\n row.tenant_id === undefined\n ? undefined\n : (row.tenant_id as string | null),\n tags: parseAiUsageTags(row.tags),\n timestamp: normalizeAiUsageTimestamp(row.created_at),\n }));\n }\n\n /**\n * Summarize persisted AI usage records by a grouping dimension.\n */\n async summarizeAiUsage(\n options: AiUsageSummaryOptions = {},\n ): Promise<Record<string, AiUsageStats>> {\n if (!this._db) {\n throw new Error(\n `AI usage requires a database configuration. ` +\n `Please call initialize() with a db option before querying usage.`,\n );\n }\n\n const groupBy = options.groupBy ?? 'model';\n const bucketExpression =\n groupBy === 'provider'\n ? `provider`\n : groupBy === 'model'\n ? `provider || ':' || model`\n : groupBy === 'class'\n ? `COALESCE(class_name, 'unknown')`\n : groupBy === 'tenant'\n ? `COALESCE(tenant_id, 'global')`\n : groupBy === 'operation'\n ? `operation`\n : `substr(CAST(created_at AS TEXT), 1, 10)`;\n\n const { conditions, params } = buildAiUsageWhereClause(options);\n\n let sql = `\n SELECT ${bucketExpression} AS bucket,\n COUNT(*) AS call_count,\n COALESCE(SUM(prompt_tokens), 0) AS prompt_tokens,\n COALESCE(SUM(completion_tokens), 0) AS completion_tokens,\n COALESCE(SUM(total_tokens), 0) AS total_tokens,\n COALESCE(SUM(duration), 0) AS total_duration,\n COALESCE(SUM(estimated_cost), 0) AS estimated_cost,\n MAX(created_at) AS last_used\n FROM _smrt_ai_usage\n `;\n\n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`;\n }\n\n sql += ' GROUP BY bucket ORDER BY bucket ASC';\n\n const rows = getQueryRows(await this._db.query(sql, ...params));\n const summary: Record<string, AiUsageStats> = {};\n\n for (const row of rows) {\n const bucket = String(row.bucket);\n summary[bucket] = {\n callCount: toSafeInteger(row.call_count ?? 0, 'AI usage call count'),\n promptTokens: toSafeInteger(\n row.prompt_tokens ?? 0,\n 'AI usage prompt-token total',\n ),\n completionTokens: toSafeInteger(\n row.completion_tokens ?? 0,\n 'AI usage completion-token total',\n ),\n totalTokens: toSafeInteger(\n row.total_tokens ?? 0,\n 'AI usage token total',\n ),\n totalDuration: toSafeInteger(\n row.total_duration ?? 0,\n 'AI usage duration total',\n ),\n estimatedCost: Number(row.estimated_cost ?? 0),\n lastUsed: row.last_used ? new Date(String(row.last_used)).getTime() : 0,\n };\n }\n\n return summary;\n }\n\n /**\n * Gets the signal bus instance\n *\n * @returns Signal bus if signals are enabled, undefined otherwise\n */\n get signalBus(): SignalBus | undefined {\n return this._signalBus;\n }\n\n /**\n * Cleanup method to prevent memory leaks\n *\n * Unregisters all adapters from the signal bus that were registered\n * by this instance. Call this when the SmrtClass instance is no longer\n * needed to prevent memory leaks.\n *\n * @example\n * ```typescript\n * const product = new Product({ name: 'Widget' });\n * await product.initialize();\n * // ... use product ...\n * product.destroy(); // Clean up when done\n * ```\n */\n destroy(): void {\n // Only unregister adapters if we own the bus (not shared)\n if (this._signalBus && !this.options.signals?.bus) {\n for (const adapter of this._registeredAdapters) {\n this._signalBus.unregister(adapter);\n }\n this._registeredAdapters = [];\n }\n\n // TODO: If SmrtClass grows a broader async teardown lifecycle, move\n // AI usage handler cleanup there alongside other connection-bound state.\n this._aiUsageCollector = undefined;\n this._aiUsageHandlers = [];\n }\n\n private mergeAiUsageConfig(\n globalConfig: GlobalSignalConfig,\n ): ResolvedAiUsageConfig {\n const globalUsage = globalConfig.usage ?? {};\n const instanceUsage = this.options.usage ?? {};\n\n return {\n enabled: instanceUsage.enabled ?? globalUsage.enabled ?? true,\n persist: instanceUsage.persist ?? globalUsage.persist ?? true,\n estimateCosts:\n instanceUsage.estimateCosts ?? globalUsage.estimateCosts ?? true,\n costRates: {\n ...(globalUsage.costRates ?? {}),\n ...(instanceUsage.costRates ?? {}),\n },\n handlers: [\n ...(globalUsage.handlers ?? []),\n ...(instanceUsage.handlers ?? []),\n ],\n };\n }\n\n private initializeAiUsageHandlers(config: ResolvedAiUsageConfig): void {\n this._aiUsageCollector = undefined;\n this._aiUsageHandlers = [];\n\n if (!config.enabled) {\n return;\n }\n\n this._aiUsageCollector = new AiUsageCollector();\n this._aiUsageHandlers.push(this._aiUsageCollector);\n\n if (config.persist && this._db) {\n this._aiUsageHandlers.push(new AiUsagePersistenceHandler(this._db));\n }\n\n this._aiUsageHandlers.push(...config.handlers);\n }\n\n private async handleAiUsageCallback(\n event: unknown,\n aiConfig: Record<string, unknown>,\n usageConfig: ResolvedAiUsageConfig,\n ): Promise<void> {\n if (!usageConfig.enabled || this._aiUsageHandlers.length === 0) {\n return;\n }\n\n const normalizedEvent = this.normalizeAiUsageEvent(event, aiConfig);\n if (!normalizedEvent) {\n return;\n }\n\n if (usageConfig.estimateCosts) {\n normalizedEvent.estimatedCost = estimateAiUsageCost(\n normalizedEvent.provider,\n normalizedEvent.model,\n normalizedEvent.usage,\n usageConfig.costRates,\n );\n }\n\n const results = await Promise.allSettled(\n this._aiUsageHandlers.map((handler) => handler.handle(normalizedEvent)),\n );\n\n for (const result of results) {\n if (result.status === 'rejected') {\n logger.warn(\n `[smrt] AI usage handler failed for ${normalizedEvent.provider}:${normalizedEvent.model}: ${\n result.reason instanceof Error\n ? result.reason.message\n : String(result.reason)\n }`,\n );\n }\n }\n }\n\n private normalizeAiUsageEvent(\n event: unknown,\n aiConfig: Record<string, unknown>,\n ): SmrtAiUsageEvent | undefined {\n const raw = (event ?? {}) as Record<string, unknown>;\n const provider = firstString(\n raw.provider,\n raw.type,\n aiConfig.provider,\n aiConfig.type,\n );\n const model = firstString(\n raw.model,\n raw.defaultModel,\n aiConfig.model,\n aiConfig.defaultModel,\n );\n const operation =\n firstString(raw.operation, raw.kind, raw.method) ?? 'unknown';\n const usage =\n normalizeIncomingAiUsageTokens(raw.usage) ??\n normalizeIncomingAiUsageTokens(raw.tokenUsage) ??\n normalizeIncomingAiUsageTokens({\n promptTokens: raw.promptTokens,\n completionTokens: raw.completionTokens,\n totalTokens: raw.totalTokens,\n });\n\n if (!provider || !model) {\n return undefined;\n }\n\n const duration =\n firstNumber(raw.duration, raw.durationMs, raw.latency) ?? 0;\n const tenantId =\n 'tenantId' in this &&\n (this as { tenantId?: string | null }).tenantId !== undefined\n ? ((this as { tenantId?: string | null }).tenantId ?? null)\n : undefined;\n\n return {\n provider,\n model,\n operation,\n usage,\n duration,\n timestamp: normalizeAiUsageTimestamp(raw.timestamp),\n tags: normalizeAiUsageTags(raw.tags),\n className: this._className,\n tenantId,\n };\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Collection access for generated SvelteKit routes (#3416).
3
+ *
4
+ * Every generated SvelteKit route that needs collections embeds a short
5
+ * prelude: it imports the generated `smrt-register` module, imports the
6
+ * application's config module (`$lib/server/smrt` by default) as a namespace,
7
+ * and destructures `getCollection`/`getSmrtConfig` from
8
+ * {@link createGeneratedCollectionAccess} over that namespace. The
9
+ * application therefore exports only its `runtime`, never the accessors.
10
+ *
11
+ * Resolution happens on every call and nothing is cached here: under
12
+ * `database-rls` isolation the runtime's collection options carry the
13
+ * request's transaction-bound database, which must never outlive the request.
14
+ */
15
+ import type { SmrtClassOptions } from './class';
16
+ import type { SmrtCollection } from './collection';
17
+ import type { SmrtObject } from './object';
18
+ /** The part of an application runtime that generated routes need. */
19
+ export interface GeneratedCollectionRuntime {
20
+ /** A collection built from the per-request class options. */
21
+ getCollection<T extends SmrtObject = SmrtObject>(className: string): Promise<SmrtCollection<T>>;
22
+ /** Per-request collection options (RLS transaction inside a request). */
23
+ classOptions(className: string): SmrtClassOptions;
24
+ }
25
+ /** Accessors a generated route destructures from its prelude. */
26
+ export interface GeneratedCollectionAccess {
27
+ getCollection<T extends SmrtObject = SmrtObject>(className: string): Promise<SmrtCollection<T>>;
28
+ getSmrtConfig(className: string): SmrtClassOptions;
29
+ }
30
+ /**
31
+ * Build the accessors a generated route's prelude destructures.
32
+ *
33
+ * - A `getCollection`/`getSmrtConfig` function exported by the application
34
+ * module is honoured first (deprecated, warned once per module).
35
+ * - Otherwise the exported `runtime`'s `getCollection()`/`classOptions()` is
36
+ * used.
37
+ * - A module exporting neither fails the call with an error naming the module.
38
+ *
39
+ * @param applicationModule - namespace of the application's server config module
40
+ * @param source - project-relative path of that module, for messages
41
+ */
42
+ export declare function createGeneratedCollectionAccess(applicationModule: unknown, source: string): GeneratedCollectionAccess;
43
+ //# sourceMappingURL=generated-collection-access.d.ts.map