@happyvertical/smrt-core 0.40.70 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -3
- package/agents/change-feed.md +1 -1
- package/agents/schema-paths.md +561 -32
- package/dist/browser.js +5 -4
- package/dist/cascade.d.ts +120 -0
- package/dist/cascade.d.ts.map +1 -0
- package/dist/cascade.js +430 -0
- package/dist/cascade.js.map +1 -0
- package/dist/change-feed.d.ts +34 -2
- package/dist/change-feed.d.ts.map +1 -1
- package/dist/change-feed.js +54 -13
- package/dist/change-feed.js.map +1 -1
- package/dist/change-signals.d.ts.map +1 -1
- package/dist/change-signals.js +8 -1
- package/dist/change-signals.js.map +1 -1
- package/dist/class.d.ts +36 -3
- package/dist/class.d.ts.map +1 -1
- package/dist/class.js +69 -14
- package/dist/class.js.map +1 -1
- package/dist/collection-cache.js +0 -0
- package/dist/collection-cache.js.map +1 -1
- package/dist/collection.d.ts +29 -0
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +68 -16
- package/dist/collection.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/decorators/index.d.ts +63 -5
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +37 -5
- package/dist/decorators/index.js.map +1 -1
- package/dist/dispatch/collections/Dispatches.d.ts.map +1 -1
- package/dist/dispatch/collections/Dispatches.js +21 -6
- package/dist/dispatch/collections/Dispatches.js.map +1 -1
- package/dist/dispatch/models/Dispatch.d.ts +1 -1
- package/dist/dispatch/models/Dispatch.d.ts.map +1 -1
- package/dist/dispatch/models/Dispatch.js +3 -2
- package/dist/dispatch/models/Dispatch.js.map +1 -1
- package/dist/dispatch/models/DispatchSubscription.d.ts +1 -1
- package/dist/dispatch/models/DispatchSubscription.d.ts.map +1 -1
- package/dist/dispatch/models/DispatchSubscription.js +2 -1
- package/dist/dispatch/models/DispatchSubscription.js.map +1 -1
- package/dist/dispatch/types.d.ts +5 -0
- package/dist/dispatch/types.d.ts.map +1 -1
- package/dist/embedded-write-queue.d.ts +46 -0
- package/dist/embedded-write-queue.d.ts.map +1 -0
- package/dist/embedded-write-queue.js +66 -0
- package/dist/embedded-write-queue.js.map +1 -0
- package/dist/embeddings/storage.d.ts +7 -0
- package/dist/embeddings/storage.d.ts.map +1 -1
- package/dist/embeddings/storage.js +31 -13
- package/dist/embeddings/storage.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/hierarchical.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/interceptors.d.ts +21 -0
- package/dist/interceptors.d.ts.map +1 -1
- package/dist/interceptors.js +27 -1
- package/dist/interceptors.js.map +1 -1
- package/dist/learning/memory.d.ts.map +1 -1
- package/dist/learning/memory.js +5 -4
- package/dist/learning/memory.js.map +1 -1
- package/dist/manifest/generator.d.ts.map +1 -1
- package/dist/manifest/generator.js +4 -7
- package/dist/manifest/generator.js.map +1 -1
- package/dist/manifest/static-manifest.js +10 -10
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest.json +19 -19
- package/dist/migrations/differ.d.ts +9 -1
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +12 -7
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/index.d.ts +2 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +3 -1
- package/dist/migrations/integer-width.d.ts +99 -0
- package/dist/migrations/integer-width.d.ts.map +1 -0
- package/dist/migrations/integer-width.js +237 -0
- package/dist/migrations/integer-width.js.map +1 -0
- package/dist/migrations/minor-units.d.ts +162 -0
- package/dist/migrations/minor-units.d.ts.map +1 -0
- package/dist/migrations/minor-units.js +383 -0
- package/dist/migrations/minor-units.js.map +1 -0
- package/dist/migrations/tracker.d.ts.map +1 -1
- package/dist/migrations/tracker.js +28 -20
- package/dist/migrations/tracker.js.map +1 -1
- package/dist/migrations.js +3 -1
- package/dist/object.d.ts +98 -5
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +138 -30
- package/dist/object.js.map +1 -1
- package/dist/query-bounds.d.ts +5 -1
- package/dist/query-bounds.d.ts.map +1 -1
- package/dist/query-bounds.js +5 -1
- package/dist/query-bounds.js.map +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +3 -1
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/manifest-field-merge.d.ts +12 -0
- package/dist/registry/manifest-field-merge.d.ts.map +1 -1
- package/dist/registry/manifest-field-merge.js +14 -2
- package/dist/registry/manifest-field-merge.js.map +1 -1
- package/dist/registry/schema-builder.d.ts +22 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +205 -165
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/types.d.ts +3 -2
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry.d.ts +41 -46
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +61 -83
- package/dist/registry.js.map +1 -1
- package/dist/scanner/manifest-generator.d.ts +45 -0
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +92 -28
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/schema/conflict-target.d.ts +104 -0
- package/dist/schema/conflict-target.d.ts.map +1 -0
- package/dist/schema/conflict-target.js +129 -0
- package/dist/schema/conflict-target.js.map +1 -0
- package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/duckdb-strategy.js +3 -1
- package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
- package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/postgres-strategy.js +14 -1
- package/dist/schema/ddl/postgres-strategy.js.map +1 -1
- package/dist/schema/generator.d.ts +245 -41
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +397 -241
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/index-utils.d.ts +120 -0
- package/dist/schema/index-utils.d.ts.map +1 -1
- package/dist/schema/index-utils.js +242 -1
- package/dist/schema/index-utils.js.map +1 -1
- package/dist/schema/index.d.ts +2 -3
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +2 -3
- package/dist/schema/live-parity.d.ts +3 -1
- package/dist/schema/live-parity.d.ts.map +1 -1
- package/dist/schema/live-parity.js +23 -2
- package/dist/schema/live-parity.js.map +1 -1
- package/dist/schema/system-table-shapes.d.ts +13 -0
- package/dist/schema/system-table-shapes.d.ts.map +1 -1
- package/dist/schema/system-table-shapes.js +14 -1
- package/dist/schema/system-table-shapes.js.map +1 -1
- package/dist/schema/types.d.ts +17 -10
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/utils.d.ts +1 -1
- package/dist/schema/utils.d.ts.map +1 -1
- package/dist/schema/utils.js +3 -3
- package/dist/schema/utils.js.map +1 -1
- package/dist/schema.js +2 -3
- package/dist/smrt-knowledge.json +9 -9
- package/dist/system/compatibility.d.ts +42 -0
- package/dist/system/compatibility.d.ts.map +1 -1
- package/dist/system/compatibility.js +182 -9
- package/dist/system/compatibility.js.map +1 -1
- package/dist/system/index.d.ts +1 -0
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +3 -2
- package/dist/system/retention.d.ts +237 -0
- package/dist/system/retention.d.ts.map +1 -0
- package/dist/system/retention.js +497 -0
- package/dist/system/retention.js.map +1 -0
- package/dist/system/schema.d.ts +102 -16
- package/dist/system/schema.d.ts.map +1 -1
- package/dist/system/schema.js +85 -47
- package/dist/system/schema.js.map +1 -1
- package/dist/system/types.d.ts +0 -2
- package/dist/system/types.d.ts.map +1 -1
- package/dist/testing/database.d.ts.map +1 -1
- package/dist/testing/database.js.map +1 -1
- package/dist/utils/safe-integer.d.ts +19 -0
- package/dist/utils/safe-integer.d.ts.map +1 -0
- package/dist/utils/safe-integer.js +31 -0
- package/dist/utils/safe-integer.js.map +1 -0
- package/dist/utils.d.ts +2 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +6 -3
- package/dist/utils.js.map +1 -1
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +4 -62
- package/dist/vite-plugin/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/schema/override-system.d.ts +0 -43
- package/dist/schema/override-system.d.ts.map +0 -1
- package/dist/schema/override-system.js +0 -205
- package/dist/schema/override-system.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3,11 +3,15 @@ import { DEFAULT_AI_COST_RATES, estimateAiUsageCost } from "./adapters/cost-rate
|
|
|
3
3
|
import { MetricsAdapter } from "./adapters/metrics.js";
|
|
4
4
|
import { PubSubAdapter } from "./adapters/pubsub.js";
|
|
5
5
|
import "./adapters/index.js";
|
|
6
|
+
import { classifyDatabaseError, classifyDialectMessage, isAbortedTransactionError, isDeterministicDatabaseError, isNotNullViolationError, isTransientDatabaseError, isUniqueViolationError } from "./db-errors.js";
|
|
7
|
+
import { AIError, ConfigurationError, DatabaseError, ErrorUtils, FilesystemError, NetworkError, RuntimeError, SmrtError, TenantIsolationError, ValidationError, ValidationReport, ValidationUtils } from "./errors.js";
|
|
8
|
+
import { buildCascadePlan, cascadeReferencesTo, normalizeOnDelete, runCascadeDelete } from "./cascade.js";
|
|
6
9
|
import { CACHE_INVALIDATION_CHANNEL, broadcastCacheInvalidation, ensureCacheInvalidationListener, getCacheGeneration, invalidateCollectionCache, resetCollectionCache, resolveDbCacheKey, stopCacheInvalidationListeners } from "./collection-cache.js";
|
|
7
10
|
import { CHANGE_SIGNAL_CHANNEL, resetChangeSignals, stopChangeSignalListeners, subscribeToChangeSignals } from "./change-signals.js";
|
|
8
11
|
import { isTenantScopedClassResolved, resolveDispatchTenantId, resolveDispatchTenantScope, setDispatchTenantResolver, setTenantScopedClassResolver } from "./dispatch/tenant-resolver.js";
|
|
9
|
-
import { GlobalInterceptors, createInterceptorContext } from "./interceptors.js";
|
|
12
|
+
import { GlobalInterceptors, createInterceptorContext, resolveGetStringFilter } from "./interceptors.js";
|
|
10
13
|
import { detectEngine, generateDDLForEngine } from "./schema/ddl/index.js";
|
|
14
|
+
import { SYSTEM_TABLE_NAMES, getSystemTableShapes } from "./schema/system-table-shapes.js";
|
|
11
15
|
import { CHANGE_FEED_INTERCEPTOR_NAME, CHANGE_FEED_TABLE, DEFAULT_CHANGES_LIMIT, MAX_CHANGES_LIMIT, appendChange, bumpChangeFeed, ensureChangeFeedTable, getChangesSince, getTableVersion, getTenantScopedChangesSince, pruneChangeFeed, registerChangeFeedWriter, resetChangeFeedWarnings, unregisterChangeFeedWriter } from "./change-feed.js";
|
|
12
16
|
import { applyOneToManyChildAccessors, childAccessorName } from "./child-accessors.js";
|
|
13
17
|
import { config } from "./config.js";
|
|
@@ -16,13 +20,11 @@ import { createFilesystemAdapter, registerFilesystemAdapterFactory } from "./fil
|
|
|
16
20
|
import { DEFAULT_POSTGRES_TIMEOUTS, POSTGRES_TIMEOUT_ENV_VARS, applyPostgresRuntimeTimeouts, resolvePostgresTimeouts } from "./postgres-timeouts.js";
|
|
17
21
|
import { SignalSanitizer } from "./signals/sanitizer.js";
|
|
18
22
|
import { SignalBus } from "./signals/bus.js";
|
|
19
|
-
import { assertPostgresSystemTimestampsCurrent, ensureDispatchSubscriptionsSystemTableCompatibility, ensureDispatchSystemTableCompatibility, ensureJobEventsSystemTableCompatibility, ensureJobsSystemTableCompatibility, ensureLegacySystemTableCompatibility, getDatabaseEngine, migratePostgresSystemTimestamps, planPostgresSystemTimestampMigrations, tableExists } from "./system/compatibility.js";
|
|
23
|
+
import { assertPostgresSystemTimestampsCurrent, ensureBootstrapSystemTableCompatibility, ensureDeferredSystemTableCompatibility, ensureDispatchSubscriptionsSystemTableCompatibility, ensureDispatchSystemTableCompatibility, ensureJobEventsSystemTableCompatibility, ensureJobsSystemTableCompatibility, ensureLegacySystemTableCompatibility, getDatabaseEngine, migratePostgresSystemTimestamps, planPostgresSystemTimestampMigrations, tableExists } from "./system/compatibility.js";
|
|
20
24
|
import { SmrtClass } from "./class.js";
|
|
21
25
|
import { DEFAULT_EMBEDDING_CONFIG, EmbeddingProvider } from "./embeddings/provider.js";
|
|
22
26
|
import { CosineSimilarity } from "./embeddings/similarity.js";
|
|
23
27
|
import { EmbeddingStorage } from "./embeddings/storage.js";
|
|
24
|
-
import { classifyDatabaseError, classifyDialectMessage, isAbortedTransactionError, isDeterministicDatabaseError, isNotNullViolationError, isTransientDatabaseError, isUniqueViolationError } from "./db-errors.js";
|
|
25
|
-
import { AIError, ConfigurationError, DatabaseError, ErrorUtils, FilesystemError, NetworkError, RuntimeError, SmrtError, TenantIsolationError, ValidationError, ValidationReport, ValidationUtils } from "./errors.js";
|
|
26
28
|
import { DEFAULT_LIST_LIMIT, DEFAULT_LIST_ORDER_BY, MAX_LIST_LIMIT, QueryBoundsError, QueryOrderByError, buildDefaultListOrderBy, resolveListLimit, resolveListOffset } from "./query-bounds.js";
|
|
27
29
|
import { applyPendingDecoratorRegistrations, registerCompatibleFieldDecorator } from "./decorators/compatibility.js";
|
|
28
30
|
import { createQualifiedName, getClassName, getPackageFromQualifiedName, isFromPackage, isQualifiedName, isType, parseQualifiedName, qualifiedNamesEqual } from "./utils/qualified-names.js";
|
|
@@ -72,11 +74,11 @@ import { createSmrtClient } from "./runtime/client.js";
|
|
|
72
74
|
import { SmrtMCPServer, createMCPServer } from "./runtime/mcp.js";
|
|
73
75
|
import { createSmrtServer } from "./runtime/server.js";
|
|
74
76
|
import "./runtime/index.js";
|
|
75
|
-
import { getSystemTableShapes } from "./schema/system-table-shapes.js";
|
|
76
77
|
import { LiveSchemaParityError, checkLiveSchemaParity } from "./schema/live-parity.js";
|
|
77
78
|
import "./signals/index.js";
|
|
79
|
+
import { DEFAULT_RETENTION_POLICY, clearRetentionTasks, getRetentionTasks, pruneAiUsage, pruneExpiredContexts, registerRetentionTask, runRetentionSweep, unregisterRetentionTask } from "./system/retention.js";
|
|
78
80
|
import "./system/index.js";
|
|
79
81
|
import { getTestDatabase } from "./testing/database.js";
|
|
80
82
|
import "./tools/index.js";
|
|
81
83
|
import { smrtPlugin } from "./vite-plugin/index.js";
|
|
82
|
-
export { AIError, APIGenerator, AiUsageCollector, AiUsagePersistenceHandler, CACHE_INVALIDATION_CHANNEL, CHANGE_FEED_INTERCEPTOR_NAME, CHANGE_FEED_TABLE, CHANGE_SIGNAL_CHANNEL, CLIGenerator, ConfigurationError, ContentHasher, CosineSimilarity, DEFAULT_AI_COST_RATES, DEFAULT_CHANGES_LIMIT, DEFAULT_EMBEDDING_CONFIG, DEFAULT_EVENTS_HEARTBEAT_MS, DEFAULT_EVENTS_MAX_SUBSCRIBERS, DEFAULT_EVENTS_RETRY_AFTER_SECONDS, DEFAULT_LEARNING_CONFIG, DEFAULT_LIST_LIMIT, DEFAULT_LIST_ORDER_BY, DEFAULT_POSTGRES_TIMEOUTS, DatabaseError, Dispatch, DispatchBus, DispatchCollection, DispatchSubscription, DispatchSubscriptionCollection, EmbeddingProvider, EmbeddingStorage, ErrorUtils, FilesystemError, GlobalInterceptors, LearningMemory, LiveSchemaParityError, MAX_CHANGES_LIMIT, MAX_LIST_LIMIT, MAX_SYNC_APPLY_BATCH_SIZE, MCPGenerator, MCP_STABLE_CATALOG_TTL_MS, MODULE_DOC_HASH_PREFIX, ManifestBuilder, ManifestGenerator, ManifestManager, MetricsAdapter, NetworkError, ObjectRegistry, POSTGRES_TIMEOUT_ENV_VARS, PRIVATE_READ_CACHE_CONTROL, PubSubAdapter, QueryBoundsError, QueryOrderByError, RuntimeError, SMRT_COLLECTION_BASE_NAMES, SMRT_CUSTOM_ACTION_ERROR_METADATA_KEY, SYNC_APPLY_ROUTE_SEGMENTS, SYNC_APPLY_UUID_PATTERN, SchemaComparer, SignalBus, SignalSanitizer, SmrtClass, SmrtCollection, SmrtError, SmrtHierarchical, SmrtJunction, SmrtMCPServer, SmrtObject, SmrtPolymorphicAssociation, TenantIsolationError, ValidationError, ValidationReport, ValidationUtils, appendChange, applyOneToManyChildAccessors, applyPendingDecoratorRegistrations, applyPostgresRuntimeTimeouts, applySyncWritablePolicy, assertPostgresSystemTimestampsCurrent, broadcastCacheInvalidation, buildChangeEventStream, buildCustomActionInputSchema, buildCustomActionInvocationArgs, buildDefaultListOrderBy, buildDomainKnowledgeManifest, bumpChangeFeed, canonicalReadRepresentation, changeEventSubscribersAtCapacity, checkLiveSchemaParity, childAccessorName, classifyDatabaseError, classifyDialectMessage, clone, computeBodyEtag, computeRuntimeWebManifestHash, computeTableVersionEtag, conditionalJsonResponse, config, convertTypeToJsonSchema, createDispatchBus, createFilesystemAdapter, createInterceptorContext, createMCPServer, createQualifiedName, createRestServer, createSmrtClient, createSmrtServer, crossPackageRef, customActionParameterInputName, detectEngine, discoverManifestEntry, ensureCacheInvalidationListener, ensureChangeFeedTable, ensureDispatchSubscriptionsSystemTableCompatibility, ensureDispatchSystemTableCompatibility, ensureJobEventsSystemTableCompatibility, ensureJobsSystemTableCompatibility, ensureLegacySystemTableCompatibility, estimateAiUsageCost, eventStreamCapacityExceededResponse, executeCollectionReadPlan, executeToolCall, executeToolCalls, field, findManifestEntryByQualifiedName, foreignKey, formatToolResults, generateDDLForEngine, generateOpenAPISpec, generateSchemaDiff, generateToolFromMethod, generateToolManifest, getAdapterInfo, getCLIHandler, getCacheGeneration, getChangesSince, getClassConfigResolvers, getClassName, getConfigResolver, getDatabaseEngine, getManifest, getPackageFromQualifiedName, getSQLFromDiff, getSystemTableShapes, getTableVersion, getTenantScopedChangesSince, getTestDatabase, hasActionableChanges, ifNoneMatchHasConcreteMatch, ifNoneMatchSatisfied, importOptionalDependency, invalidateCollectionCache, isAbortedTransactionError, isAdvisoryOnlyChange, isDatabaseInterface, isDeterministicDatabaseError, isFromPackage, isLazyConfigSentinel, isManualOrAdvisoryChange, isNotNullViolationError, isQualifiedName, isSmrtCollectionExtendsName, isStaleWrite, isTenantScopedClassResolved, isTransientDatabaseError, isType, isUniqueViolationError, isValid, listConfigResolvers, loadExternalManifest, loadExternalManifestSync, loadLocalTestManifestSync, loadManifestFromPathSync, manifest, manyToMany, meta, migratePostgresSystemTimestamps, normalizeCustomActionFailure, normalizeEventsMaxSubscribers, normalizeTypedHttpError, oneToMany, parse, parseQualifiedName, parseSyncApplyBatch, payloadMatchesRow, planPostgresSystemTimestampMigrations, processSyncApplyBatch, pruneChangeFeed, qualifiedNamesEqual, readAgentModuleDocs, registerChangeFeedWriter, registerCompatibleFieldDecorator, registerConfigResolver, registerFilesystemAdapterFactory, registerOptionalDependency, resetChangeFeedWarnings, resetChangeSignals, resetCollectionCache, resetConfigResolvers, resetVerifiedTables, resolveAgentModuleDocPaths, resolveCustomActionMetadata, resolveDatabase, resolveDbCacheKey, resolveDispatchTenantId, resolveDispatchTenantScope, resolveLazyConfig, resolveListLimit, resolveListOffset, resolveMCPToolListCacheHint, resolvePostgresTimeouts, resolveReadCacheControl, resolveTenantEtagDiscriminator, runWithTenantGate, safeParse, safeStringify, setDispatchTenantResolver, setTenantEntryPointRunner, setTenantScopedClassResolver, setupCLI, setupSwaggerUI, shouldIncludeMethod, signalVisibleToTenant, smrt, smrtPlugin, smrt as smrtRegistry, sortMCPTools, startRestServer, staticManifest, stopCacheInvalidationListeners, stopChangeSignalListeners, stringify, subscribeToChangeSignals, tableExists, tryReserveChangeEventSubscriberSlot, unregisterChangeFeedWriter, unregisterConfigResolver, validateSyncApplyItem, validateToolCall, versionConditionalResponse, warnIfSharedCacheNeutralized };
|
|
84
|
+
export { AIError, APIGenerator, AiUsageCollector, AiUsagePersistenceHandler, CACHE_INVALIDATION_CHANNEL, CHANGE_FEED_INTERCEPTOR_NAME, CHANGE_FEED_TABLE, CHANGE_SIGNAL_CHANNEL, CLIGenerator, ConfigurationError, ContentHasher, CosineSimilarity, DEFAULT_AI_COST_RATES, DEFAULT_CHANGES_LIMIT, DEFAULT_EMBEDDING_CONFIG, DEFAULT_EVENTS_HEARTBEAT_MS, DEFAULT_EVENTS_MAX_SUBSCRIBERS, DEFAULT_EVENTS_RETRY_AFTER_SECONDS, DEFAULT_LEARNING_CONFIG, DEFAULT_LIST_LIMIT, DEFAULT_LIST_ORDER_BY, DEFAULT_POSTGRES_TIMEOUTS, DEFAULT_RETENTION_POLICY, DatabaseError, Dispatch, DispatchBus, DispatchCollection, DispatchSubscription, DispatchSubscriptionCollection, EmbeddingProvider, EmbeddingStorage, ErrorUtils, FilesystemError, GlobalInterceptors, LearningMemory, LiveSchemaParityError, MAX_CHANGES_LIMIT, MAX_LIST_LIMIT, MAX_SYNC_APPLY_BATCH_SIZE, MCPGenerator, MCP_STABLE_CATALOG_TTL_MS, MODULE_DOC_HASH_PREFIX, ManifestBuilder, ManifestGenerator, ManifestManager, MetricsAdapter, NetworkError, ObjectRegistry, POSTGRES_TIMEOUT_ENV_VARS, PRIVATE_READ_CACHE_CONTROL, PubSubAdapter, QueryBoundsError, QueryOrderByError, RuntimeError, SMRT_COLLECTION_BASE_NAMES, SMRT_CUSTOM_ACTION_ERROR_METADATA_KEY, SYNC_APPLY_ROUTE_SEGMENTS, SYNC_APPLY_UUID_PATTERN, SYSTEM_TABLE_NAMES, SchemaComparer, SignalBus, SignalSanitizer, SmrtClass, SmrtCollection, SmrtError, SmrtHierarchical, SmrtJunction, SmrtMCPServer, SmrtObject, SmrtPolymorphicAssociation, TenantIsolationError, ValidationError, ValidationReport, ValidationUtils, appendChange, applyOneToManyChildAccessors, applyPendingDecoratorRegistrations, applyPostgresRuntimeTimeouts, applySyncWritablePolicy, assertPostgresSystemTimestampsCurrent, broadcastCacheInvalidation, buildCascadePlan, buildChangeEventStream, buildCustomActionInputSchema, buildCustomActionInvocationArgs, buildDefaultListOrderBy, buildDomainKnowledgeManifest, bumpChangeFeed, canonicalReadRepresentation, cascadeReferencesTo, changeEventSubscribersAtCapacity, checkLiveSchemaParity, childAccessorName, classifyDatabaseError, classifyDialectMessage, clearRetentionTasks, clone, computeBodyEtag, computeRuntimeWebManifestHash, computeTableVersionEtag, conditionalJsonResponse, config, convertTypeToJsonSchema, createDispatchBus, createFilesystemAdapter, createInterceptorContext, createMCPServer, createQualifiedName, createRestServer, createSmrtClient, createSmrtServer, crossPackageRef, customActionParameterInputName, detectEngine, discoverManifestEntry, ensureBootstrapSystemTableCompatibility, ensureCacheInvalidationListener, ensureChangeFeedTable, ensureDeferredSystemTableCompatibility, ensureDispatchSubscriptionsSystemTableCompatibility, ensureDispatchSystemTableCompatibility, ensureJobEventsSystemTableCompatibility, ensureJobsSystemTableCompatibility, ensureLegacySystemTableCompatibility, estimateAiUsageCost, eventStreamCapacityExceededResponse, executeCollectionReadPlan, executeToolCall, executeToolCalls, field, findManifestEntryByQualifiedName, foreignKey, formatToolResults, generateDDLForEngine, generateOpenAPISpec, generateSchemaDiff, generateToolFromMethod, generateToolManifest, getAdapterInfo, getCLIHandler, getCacheGeneration, getChangesSince, getClassConfigResolvers, getClassName, getConfigResolver, getDatabaseEngine, getManifest, getPackageFromQualifiedName, getRetentionTasks, getSQLFromDiff, getSystemTableShapes, getTableVersion, getTenantScopedChangesSince, getTestDatabase, hasActionableChanges, ifNoneMatchHasConcreteMatch, ifNoneMatchSatisfied, importOptionalDependency, invalidateCollectionCache, isAbortedTransactionError, isAdvisoryOnlyChange, isDatabaseInterface, isDeterministicDatabaseError, isFromPackage, isLazyConfigSentinel, isManualOrAdvisoryChange, isNotNullViolationError, isQualifiedName, isSmrtCollectionExtendsName, isStaleWrite, isTenantScopedClassResolved, isTransientDatabaseError, isType, isUniqueViolationError, isValid, listConfigResolvers, loadExternalManifest, loadExternalManifestSync, loadLocalTestManifestSync, loadManifestFromPathSync, manifest, manyToMany, meta, migratePostgresSystemTimestamps, normalizeCustomActionFailure, normalizeEventsMaxSubscribers, normalizeOnDelete, normalizeTypedHttpError, oneToMany, parse, parseQualifiedName, parseSyncApplyBatch, payloadMatchesRow, planPostgresSystemTimestampMigrations, processSyncApplyBatch, pruneAiUsage, pruneChangeFeed, pruneExpiredContexts, qualifiedNamesEqual, readAgentModuleDocs, registerChangeFeedWriter, registerCompatibleFieldDecorator, registerConfigResolver, registerFilesystemAdapterFactory, registerOptionalDependency, registerRetentionTask, resetChangeFeedWarnings, resetChangeSignals, resetCollectionCache, resetConfigResolvers, resetVerifiedTables, resolveAgentModuleDocPaths, resolveCustomActionMetadata, resolveDatabase, resolveDbCacheKey, resolveDispatchTenantId, resolveDispatchTenantScope, resolveGetStringFilter, resolveLazyConfig, resolveListLimit, resolveListOffset, resolveMCPToolListCacheHint, resolvePostgresTimeouts, resolveReadCacheControl, resolveTenantEtagDiscriminator, runCascadeDelete, runRetentionSweep, runWithTenantGate, safeParse, safeStringify, setDispatchTenantResolver, setTenantEntryPointRunner, setTenantScopedClassResolver, setupCLI, setupSwaggerUI, shouldIncludeMethod, signalVisibleToTenant, smrt, smrtPlugin, smrt as smrtRegistry, sortMCPTools, startRestServer, staticManifest, stopCacheInvalidationListeners, stopChangeSignalListeners, stringify, subscribeToChangeSignals, tableExists, tryReserveChangeEventSubscriberSlot, unregisterChangeFeedWriter, unregisterConfigResolver, unregisterRetentionTask, validateSyncApplyItem, validateToolCall, versionConditionalResponse, warnIfSharedCacheNeutralized };
|
package/dist/interceptors.d.ts
CHANGED
|
@@ -244,6 +244,27 @@ export declare class GlobalInterceptors {
|
|
|
244
244
|
*/
|
|
245
245
|
static executeAfterDelete(instance: SmrtObject, context: InterceptorContext): Promise<void>;
|
|
246
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* Resolve a string filter to the WHERE shape `SmrtCollection.get()` gives it:
|
|
249
|
+
* a UUID string becomes `{ id }`, anything else becomes the natural-key lookup
|
|
250
|
+
* `{ slug, context: '' }`.
|
|
251
|
+
*
|
|
252
|
+
* This is the single source of truth for that string-filter contract. Any
|
|
253
|
+
* `beforeGet` interceptor that rewrites a string filter into an object filter
|
|
254
|
+
* MUST resolve it through this helper first — the tenancy interceptor used to
|
|
255
|
+
* rewrite every string to `{ id: filter, tenantId }`, which broke get-by-slug
|
|
256
|
+
* under an active tenant context (null on SQLite, a uuid cast error on
|
|
257
|
+
* PostgreSQL native-uuid id columns). See #2365.
|
|
258
|
+
*
|
|
259
|
+
* @param filter - The string filter passed to `get()`
|
|
260
|
+
* @returns The object filter `get()` would build for that string
|
|
261
|
+
*/
|
|
262
|
+
export declare function resolveGetStringFilter(filter: string): {
|
|
263
|
+
id: string;
|
|
264
|
+
} | {
|
|
265
|
+
slug: string;
|
|
266
|
+
context: string;
|
|
267
|
+
};
|
|
247
268
|
/**
|
|
248
269
|
* Helper function to create an interceptor context
|
|
249
270
|
* @internal
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interceptors.d.ts","sourceRoot":"","sources":["../src/interceptors.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"interceptors.d.ts","sourceRoot":"","sources":["../src/interceptors.ts"],"names":[],"mappings":"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"}
|
package/dist/interceptors.js
CHANGED
|
@@ -188,6 +188,32 @@ var GlobalInterceptors = class {
|
|
|
188
188
|
}
|
|
189
189
|
};
|
|
190
190
|
/**
|
|
191
|
+
* Canonical UUID shape used by `SmrtCollection.get()` to decide whether a
|
|
192
|
+
* string filter is an id or a slug.
|
|
193
|
+
*/
|
|
194
|
+
var GET_STRING_FILTER_UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
195
|
+
/**
|
|
196
|
+
* Resolve a string filter to the WHERE shape `SmrtCollection.get()` gives it:
|
|
197
|
+
* a UUID string becomes `{ id }`, anything else becomes the natural-key lookup
|
|
198
|
+
* `{ slug, context: '' }`.
|
|
199
|
+
*
|
|
200
|
+
* This is the single source of truth for that string-filter contract. Any
|
|
201
|
+
* `beforeGet` interceptor that rewrites a string filter into an object filter
|
|
202
|
+
* MUST resolve it through this helper first — the tenancy interceptor used to
|
|
203
|
+
* rewrite every string to `{ id: filter, tenantId }`, which broke get-by-slug
|
|
204
|
+
* under an active tenant context (null on SQLite, a uuid cast error on
|
|
205
|
+
* PostgreSQL native-uuid id columns). See #2365.
|
|
206
|
+
*
|
|
207
|
+
* @param filter - The string filter passed to `get()`
|
|
208
|
+
* @returns The object filter `get()` would build for that string
|
|
209
|
+
*/
|
|
210
|
+
function resolveGetStringFilter(filter) {
|
|
211
|
+
return GET_STRING_FILTER_UUID_PATTERN.test(filter) ? { id: filter } : {
|
|
212
|
+
slug: filter,
|
|
213
|
+
context: ""
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
191
217
|
* Helper function to create an interceptor context
|
|
192
218
|
* @internal
|
|
193
219
|
*/
|
|
@@ -201,6 +227,6 @@ function createInterceptorContext(className, operation, collectionName, metadata
|
|
|
201
227
|
};
|
|
202
228
|
}
|
|
203
229
|
//#endregion
|
|
204
|
-
export { GlobalInterceptors, createInterceptorContext };
|
|
230
|
+
export { GlobalInterceptors, createInterceptorContext, resolveGetStringFilter };
|
|
205
231
|
|
|
206
232
|
//# sourceMappingURL=interceptors.js.map
|
package/dist/interceptors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interceptors.js","names":[],"sources":["../src/interceptors.ts"],"sourcesContent":["/**\n * Global Interceptors System for SMRT Collections and Objects\n *\n * Provides a plugin architecture for intercepting collection and object operations.\n * This enables external packages (like @happyvertical/smrt-tenancy) to register\n * interceptors without creating circular dependencies.\n *\n * Pattern: Static methods on GlobalInterceptors class with globalThis state\n * (matches ObjectRegistry pattern for cross-module state sharing)\n *\n * @example Basic interceptor registration\n * ```typescript\n * import { GlobalInterceptors } from '@happyvertical/smrt-core';\n *\n * // Register a beforeList interceptor\n * GlobalInterceptors.register({\n * beforeList(className, options) {\n * console.log(`Listing ${className}`);\n * return options; // Return modified options\n * }\n * });\n * ```\n *\n * @example Tenancy interceptor (from smrt-tenancy)\n * ```typescript\n * GlobalInterceptors.register({\n * beforeList(className, options, context) {\n * const tenantId = getCurrentTenantId();\n * if (tenantId && isTenantScoped(className)) {\n * return {\n * ...options,\n * where: { ...options.where, tenantId }\n * };\n * }\n * return options;\n * },\n * beforeSave(instance, context) {\n * // Validate tenant on save\n * }\n * });\n * ```\n *\n * @see https://github.com/happyvertical/smrt/issues/675\n */\n\nimport type { SmrtObject } from './object.js';\n\n/**\n * Context passed to interceptors with operation metadata\n */\nexport interface InterceptorContext {\n /** Name of the SMRT class being operated on */\n className: string;\n /** Name of the collection (if applicable) */\n collectionName?: string;\n /** Timestamp of the operation */\n timestamp: Date;\n /** Operation type for debugging */\n operation: 'list' | 'get' | 'query' | 'save' | 'delete';\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Options for list operations\n */\nexport interface ListOptions {\n where?: Record<string, unknown>;\n orderBy?: string | string[];\n limit?: number;\n offset?: number;\n select?: readonly string[];\n include?: string[];\n [key: string]: unknown;\n}\n\n/**\n * Options for query operations (raw SQL)\n */\nexport interface QueryOptions {\n /** The raw SQL query */\n sql: string;\n /** Query parameters */\n params: unknown[];\n /** If true, allow raw SQL on tenant-scoped classes (requires explicit opt-in) */\n allowRawOnTenantScoped?: boolean;\n}\n\n/**\n * Result from query interceptor - can modify SQL or throw to block\n */\nexport interface QueryInterceptResult {\n sql: string;\n params: unknown[];\n}\n\n/**\n * Interface for collection/object interceptors\n *\n * All methods are optional. Return void to pass through unchanged,\n * or return modified data to transform the operation.\n */\nexport interface CollectionInterceptor {\n /** Unique identifier for this interceptor (for debugging/unregistration) */\n name?: string;\n\n /** Priority for execution order (higher = earlier). Default: 0 */\n priority?: number;\n\n /**\n * Called before list() operations\n * @param className - Name of the class being listed\n * @param options - List options (where, orderBy, limit, select, etc.)\n * @param context - Operation context\n * @returns Modified options or void to pass through\n */\n beforeList?(\n className: string,\n options: ListOptions,\n context: InterceptorContext,\n ): ListOptions | Promise<ListOptions> | void | Promise<void>;\n\n /**\n * Called after list() operations with results\n * @param className - Name of the class\n * @param results - Array of hydrated instances\n * @param context - Operation context\n * @returns Modified results or void to pass through\n */\n afterList?<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): T[] | Promise<T[]> | void | Promise<void>;\n\n /**\n * Called before get() operations\n * @param className - Name of the class\n * @param filter - ID, slug, or filter object\n * @param context - Operation context\n * @returns Modified filter or void to pass through\n */\n beforeGet?(\n className: string,\n filter: string | Record<string, unknown>,\n context: InterceptorContext,\n ):\n | string\n | Record<string, unknown>\n | Promise<string | Record<string, unknown>>\n | void\n | Promise<void>;\n\n /**\n * Called after get() operations with result\n * @param className - Name of the class\n * @param instance - Hydrated instance or null if not found\n * @param context - Operation context\n * @returns Modified instance, null, or void to pass through\n */\n afterGet?<T extends SmrtObject>(\n className: string,\n instance: T | null,\n context: InterceptorContext,\n ): T | null | Promise<T | null> | void | Promise<void>;\n\n /**\n * Called before query() operations (raw SQL)\n * @param className - Name of the class\n * @param options - Query options with SQL and params\n * @param context - Operation context\n * @returns Modified query options, or throw to block the query\n */\n beforeQuery?(\n className: string,\n options: QueryOptions,\n context: InterceptorContext,\n ):\n | QueryInterceptResult\n | Promise<QueryInterceptResult>\n | void\n | Promise<void>;\n\n /**\n * Called after query() operations with results\n * @param className - Name of the class\n * @param results - Array of hydrated instances\n * @param context - Operation context\n * @returns Modified results or void to pass through\n */\n afterQuery?<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): T[] | Promise<T[]> | void | Promise<void>;\n\n /**\n * Called before save() operations\n * @param instance - The object being saved\n * @param context - Operation context\n * @returns void (modify instance in place) or throw to block\n */\n beforeSave?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called after save() operations\n * @param instance - The saved object\n * @param context - Operation context\n */\n afterSave?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called before delete() operations\n * @param instance - The object being deleted\n * @param context - Operation context\n * @returns void or throw to block\n */\n beforeDelete?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called after delete() operations\n * @param instance - The deleted object\n * @param context - Operation context\n */\n afterDelete?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n}\n\n/**\n * Extend globalThis to include interceptors state.\n * Using globalThis ensures all module instances share the same interceptors,\n * which is critical in monorepos where the same package can be loaded\n * from different paths.\n *\n * @see ObjectRegistry for similar pattern\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtInterceptors: unknown[] | undefined;\n}\n\n/**\n * Global Interceptors Registry\n *\n * Manages collection/object interceptors for the SMRT framework.\n * External packages can register interceptors to hook into operations\n * without modifying core code.\n */\nexport class GlobalInterceptors {\n /**\n * Get the global interceptors array (lazy-initialized)\n */\n private static get interceptors(): CollectionInterceptor[] {\n if (!globalThis.__smrtInterceptors) {\n globalThis.__smrtInterceptors = [];\n }\n return globalThis.__smrtInterceptors as CollectionInterceptor[];\n }\n\n /**\n * Register an interceptor\n *\n * @param interceptor - The interceptor to register\n * @example\n * ```typescript\n * GlobalInterceptors.register({\n * name: 'tenancy',\n * priority: 100,\n * beforeList(className, options) {\n * // Add tenant filtering\n * return { ...options, where: { ...options.where, tenantId: 'xxx' } };\n * }\n * });\n * ```\n */\n static register(interceptor: CollectionInterceptor): void {\n // Check for duplicate by name\n if (interceptor.name) {\n const existing = this.interceptors.findIndex(\n (i) => i.name === interceptor.name,\n );\n if (existing >= 0) {\n // Replace existing interceptor with same name\n this.interceptors[existing] = interceptor;\n this.sortByPriority();\n return;\n }\n }\n\n this.interceptors.push(interceptor);\n this.sortByPriority();\n }\n\n /**\n * Unregister an interceptor by reference or name\n *\n * @param interceptorOrName - The interceptor instance or name to remove\n * @returns true if removed, false if not found\n */\n static unregister(\n interceptorOrName: CollectionInterceptor | string,\n ): boolean {\n const idx =\n typeof interceptorOrName === 'string'\n ? this.interceptors.findIndex((i) => i.name === interceptorOrName)\n : this.interceptors.indexOf(interceptorOrName);\n\n if (idx >= 0) {\n this.interceptors.splice(idx, 1);\n return true;\n }\n return false;\n }\n\n /**\n * Clear all interceptors (useful for testing)\n */\n static clear(): void {\n globalThis.__smrtInterceptors = [];\n }\n\n /**\n * Get all registered interceptors (readonly)\n */\n static getAll(): readonly CollectionInterceptor[] {\n return [...this.interceptors];\n }\n\n /**\n * Check if any interceptors are registered\n */\n static hasInterceptors(): boolean {\n return this.interceptors.length > 0;\n }\n\n /**\n * Sort interceptors by priority (higher priority first)\n */\n private static sortByPriority(): void {\n this.interceptors.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Execution methods - called by SmrtCollection and SmrtObject\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Execute beforeList interceptors\n * @internal Called by SmrtCollection.list()\n */\n static async executeBeforeList(\n className: string,\n options: ListOptions,\n context: InterceptorContext,\n ): Promise<ListOptions> {\n let result = options;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeList) {\n const modified = await interceptor.beforeList(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterList interceptors\n * @internal Called by SmrtCollection.list()\n */\n static async executeAfterList<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): Promise<T[]> {\n let result = results;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterList) {\n const modified = await interceptor.afterList(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified as T[];\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeGet interceptors\n * @internal Called by SmrtCollection.get()\n */\n static async executeBeforeGet(\n className: string,\n filter: string | Record<string, unknown>,\n context: InterceptorContext,\n ): Promise<string | Record<string, unknown>> {\n let result = filter;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeGet) {\n const modified = await interceptor.beforeGet(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterGet interceptors\n * @internal Called by SmrtCollection.get()\n */\n static async executeAfterGet<T extends SmrtObject>(\n className: string,\n instance: T | null,\n context: InterceptorContext,\n ): Promise<T | null> {\n let result = instance;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterGet) {\n const modified = await interceptor.afterGet(className, result, context);\n if (modified !== undefined) {\n result = modified as T | null;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeQuery interceptors\n * @internal Called by SmrtCollection.query()\n */\n static async executeBeforeQuery(\n className: string,\n options: QueryOptions,\n context: InterceptorContext,\n ): Promise<QueryInterceptResult> {\n let result: QueryInterceptResult = {\n sql: options.sql,\n params: options.params,\n };\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeQuery) {\n const modified = await interceptor.beforeQuery(\n className,\n { ...options, ...result },\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterQuery interceptors\n * @internal Called by SmrtCollection.query()\n */\n static async executeAfterQuery<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): Promise<T[]> {\n let result = results;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterQuery) {\n const modified = await interceptor.afterQuery(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified as T[];\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeSave interceptors\n * @internal Called by SmrtObject.save()\n */\n static async executeBeforeSave(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeSave) {\n await interceptor.beforeSave(instance, context);\n }\n }\n }\n\n /**\n * Execute afterSave interceptors\n * @internal Called by SmrtObject.save()\n */\n static async executeAfterSave(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.afterSave) {\n await interceptor.afterSave(instance, context);\n }\n }\n }\n\n /**\n * Execute beforeDelete interceptors\n * @internal Called by SmrtObject.delete()\n */\n static async executeBeforeDelete(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeDelete) {\n await interceptor.beforeDelete(instance, context);\n }\n }\n }\n\n /**\n * Execute afterDelete interceptors\n * @internal Called by SmrtObject.delete()\n */\n static async executeAfterDelete(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.afterDelete) {\n await interceptor.afterDelete(instance, context);\n }\n }\n }\n}\n\n/**\n * Helper function to create an interceptor context\n * @internal\n */\nexport function createInterceptorContext(\n className: string,\n operation: InterceptorContext['operation'],\n collectionName?: string,\n metadata?: Record<string, unknown>,\n): InterceptorContext {\n return {\n className,\n collectionName,\n operation,\n timestamp: new Date(),\n metadata,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"interceptors.js","names":[],"sources":["../src/interceptors.ts"],"sourcesContent":["/**\n * Global Interceptors System for SMRT Collections and Objects\n *\n * Provides a plugin architecture for intercepting collection and object operations.\n * This enables external packages (like @happyvertical/smrt-tenancy) to register\n * interceptors without creating circular dependencies.\n *\n * Pattern: Static methods on GlobalInterceptors class with globalThis state\n * (matches ObjectRegistry pattern for cross-module state sharing)\n *\n * @example Basic interceptor registration\n * ```typescript\n * import { GlobalInterceptors } from '@happyvertical/smrt-core';\n *\n * // Register a beforeList interceptor\n * GlobalInterceptors.register({\n * beforeList(className, options) {\n * console.log(`Listing ${className}`);\n * return options; // Return modified options\n * }\n * });\n * ```\n *\n * @example Tenancy interceptor (from smrt-tenancy)\n * ```typescript\n * GlobalInterceptors.register({\n * beforeList(className, options, context) {\n * const tenantId = getCurrentTenantId();\n * if (tenantId && isTenantScoped(className)) {\n * return {\n * ...options,\n * where: { ...options.where, tenantId }\n * };\n * }\n * return options;\n * },\n * beforeSave(instance, context) {\n * // Validate tenant on save\n * }\n * });\n * ```\n *\n * @see https://github.com/happyvertical/smrt/issues/675\n */\n\nimport type { SmrtObject } from './object.js';\n\n/**\n * Context passed to interceptors with operation metadata\n */\nexport interface InterceptorContext {\n /** Name of the SMRT class being operated on */\n className: string;\n /** Name of the collection (if applicable) */\n collectionName?: string;\n /** Timestamp of the operation */\n timestamp: Date;\n /** Operation type for debugging */\n operation: 'list' | 'get' | 'query' | 'save' | 'delete';\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Options for list operations\n */\nexport interface ListOptions {\n where?: Record<string, unknown>;\n orderBy?: string | string[];\n limit?: number;\n offset?: number;\n select?: readonly string[];\n include?: string[];\n [key: string]: unknown;\n}\n\n/**\n * Options for query operations (raw SQL)\n */\nexport interface QueryOptions {\n /** The raw SQL query */\n sql: string;\n /** Query parameters */\n params: unknown[];\n /** If true, allow raw SQL on tenant-scoped classes (requires explicit opt-in) */\n allowRawOnTenantScoped?: boolean;\n}\n\n/**\n * Result from query interceptor - can modify SQL or throw to block\n */\nexport interface QueryInterceptResult {\n sql: string;\n params: unknown[];\n}\n\n/**\n * Interface for collection/object interceptors\n *\n * All methods are optional. Return void to pass through unchanged,\n * or return modified data to transform the operation.\n */\nexport interface CollectionInterceptor {\n /** Unique identifier for this interceptor (for debugging/unregistration) */\n name?: string;\n\n /** Priority for execution order (higher = earlier). Default: 0 */\n priority?: number;\n\n /**\n * Called before list() operations\n * @param className - Name of the class being listed\n * @param options - List options (where, orderBy, limit, select, etc.)\n * @param context - Operation context\n * @returns Modified options or void to pass through\n */\n beforeList?(\n className: string,\n options: ListOptions,\n context: InterceptorContext,\n ): ListOptions | Promise<ListOptions> | void | Promise<void>;\n\n /**\n * Called after list() operations with results\n * @param className - Name of the class\n * @param results - Array of hydrated instances\n * @param context - Operation context\n * @returns Modified results or void to pass through\n */\n afterList?<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): T[] | Promise<T[]> | void | Promise<void>;\n\n /**\n * Called before get() operations\n * @param className - Name of the class\n * @param filter - ID, slug, or filter object\n * @param context - Operation context\n * @returns Modified filter or void to pass through\n */\n beforeGet?(\n className: string,\n filter: string | Record<string, unknown>,\n context: InterceptorContext,\n ):\n | string\n | Record<string, unknown>\n | Promise<string | Record<string, unknown>>\n | void\n | Promise<void>;\n\n /**\n * Called after get() operations with result\n * @param className - Name of the class\n * @param instance - Hydrated instance or null if not found\n * @param context - Operation context\n * @returns Modified instance, null, or void to pass through\n */\n afterGet?<T extends SmrtObject>(\n className: string,\n instance: T | null,\n context: InterceptorContext,\n ): T | null | Promise<T | null> | void | Promise<void>;\n\n /**\n * Called before query() operations (raw SQL)\n * @param className - Name of the class\n * @param options - Query options with SQL and params\n * @param context - Operation context\n * @returns Modified query options, or throw to block the query\n */\n beforeQuery?(\n className: string,\n options: QueryOptions,\n context: InterceptorContext,\n ):\n | QueryInterceptResult\n | Promise<QueryInterceptResult>\n | void\n | Promise<void>;\n\n /**\n * Called after query() operations with results\n * @param className - Name of the class\n * @param results - Array of hydrated instances\n * @param context - Operation context\n * @returns Modified results or void to pass through\n */\n afterQuery?<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): T[] | Promise<T[]> | void | Promise<void>;\n\n /**\n * Called before save() operations\n * @param instance - The object being saved\n * @param context - Operation context\n * @returns void (modify instance in place) or throw to block\n */\n beforeSave?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called after save() operations\n * @param instance - The saved object\n * @param context - Operation context\n */\n afterSave?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called before delete() operations\n * @param instance - The object being deleted\n * @param context - Operation context\n * @returns void or throw to block\n */\n beforeDelete?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called after delete() operations\n * @param instance - The deleted object\n * @param context - Operation context\n */\n afterDelete?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n}\n\n/**\n * Extend globalThis to include interceptors state.\n * Using globalThis ensures all module instances share the same interceptors,\n * which is critical in monorepos where the same package can be loaded\n * from different paths.\n *\n * @see ObjectRegistry for similar pattern\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtInterceptors: unknown[] | undefined;\n}\n\n/**\n * Global Interceptors Registry\n *\n * Manages collection/object interceptors for the SMRT framework.\n * External packages can register interceptors to hook into operations\n * without modifying core code.\n */\nexport class GlobalInterceptors {\n /**\n * Get the global interceptors array (lazy-initialized)\n */\n private static get interceptors(): CollectionInterceptor[] {\n if (!globalThis.__smrtInterceptors) {\n globalThis.__smrtInterceptors = [];\n }\n return globalThis.__smrtInterceptors as CollectionInterceptor[];\n }\n\n /**\n * Register an interceptor\n *\n * @param interceptor - The interceptor to register\n * @example\n * ```typescript\n * GlobalInterceptors.register({\n * name: 'tenancy',\n * priority: 100,\n * beforeList(className, options) {\n * // Add tenant filtering\n * return { ...options, where: { ...options.where, tenantId: 'xxx' } };\n * }\n * });\n * ```\n */\n static register(interceptor: CollectionInterceptor): void {\n // Check for duplicate by name\n if (interceptor.name) {\n const existing = this.interceptors.findIndex(\n (i) => i.name === interceptor.name,\n );\n if (existing >= 0) {\n // Replace existing interceptor with same name\n this.interceptors[existing] = interceptor;\n this.sortByPriority();\n return;\n }\n }\n\n this.interceptors.push(interceptor);\n this.sortByPriority();\n }\n\n /**\n * Unregister an interceptor by reference or name\n *\n * @param interceptorOrName - The interceptor instance or name to remove\n * @returns true if removed, false if not found\n */\n static unregister(\n interceptorOrName: CollectionInterceptor | string,\n ): boolean {\n const idx =\n typeof interceptorOrName === 'string'\n ? this.interceptors.findIndex((i) => i.name === interceptorOrName)\n : this.interceptors.indexOf(interceptorOrName);\n\n if (idx >= 0) {\n this.interceptors.splice(idx, 1);\n return true;\n }\n return false;\n }\n\n /**\n * Clear all interceptors (useful for testing)\n */\n static clear(): void {\n globalThis.__smrtInterceptors = [];\n }\n\n /**\n * Get all registered interceptors (readonly)\n */\n static getAll(): readonly CollectionInterceptor[] {\n return [...this.interceptors];\n }\n\n /**\n * Check if any interceptors are registered\n */\n static hasInterceptors(): boolean {\n return this.interceptors.length > 0;\n }\n\n /**\n * Sort interceptors by priority (higher priority first)\n */\n private static sortByPriority(): void {\n this.interceptors.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Execution methods - called by SmrtCollection and SmrtObject\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Execute beforeList interceptors\n * @internal Called by SmrtCollection.list()\n */\n static async executeBeforeList(\n className: string,\n options: ListOptions,\n context: InterceptorContext,\n ): Promise<ListOptions> {\n let result = options;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeList) {\n const modified = await interceptor.beforeList(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterList interceptors\n * @internal Called by SmrtCollection.list()\n */\n static async executeAfterList<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): Promise<T[]> {\n let result = results;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterList) {\n const modified = await interceptor.afterList(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified as T[];\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeGet interceptors\n * @internal Called by SmrtCollection.get()\n */\n static async executeBeforeGet(\n className: string,\n filter: string | Record<string, unknown>,\n context: InterceptorContext,\n ): Promise<string | Record<string, unknown>> {\n let result = filter;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeGet) {\n const modified = await interceptor.beforeGet(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterGet interceptors\n * @internal Called by SmrtCollection.get()\n */\n static async executeAfterGet<T extends SmrtObject>(\n className: string,\n instance: T | null,\n context: InterceptorContext,\n ): Promise<T | null> {\n let result = instance;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterGet) {\n const modified = await interceptor.afterGet(className, result, context);\n if (modified !== undefined) {\n result = modified as T | null;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeQuery interceptors\n * @internal Called by SmrtCollection.query()\n */\n static async executeBeforeQuery(\n className: string,\n options: QueryOptions,\n context: InterceptorContext,\n ): Promise<QueryInterceptResult> {\n let result: QueryInterceptResult = {\n sql: options.sql,\n params: options.params,\n };\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeQuery) {\n const modified = await interceptor.beforeQuery(\n className,\n { ...options, ...result },\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterQuery interceptors\n * @internal Called by SmrtCollection.query()\n */\n static async executeAfterQuery<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): Promise<T[]> {\n let result = results;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterQuery) {\n const modified = await interceptor.afterQuery(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified as T[];\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeSave interceptors\n * @internal Called by SmrtObject.save()\n */\n static async executeBeforeSave(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeSave) {\n await interceptor.beforeSave(instance, context);\n }\n }\n }\n\n /**\n * Execute afterSave interceptors\n * @internal Called by SmrtObject.save()\n */\n static async executeAfterSave(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.afterSave) {\n await interceptor.afterSave(instance, context);\n }\n }\n }\n\n /**\n * Execute beforeDelete interceptors\n * @internal Called by SmrtObject.delete()\n */\n static async executeBeforeDelete(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeDelete) {\n await interceptor.beforeDelete(instance, context);\n }\n }\n }\n\n /**\n * Execute afterDelete interceptors\n * @internal Called by SmrtObject.delete()\n */\n static async executeAfterDelete(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.afterDelete) {\n await interceptor.afterDelete(instance, context);\n }\n }\n }\n}\n\n/**\n * Canonical UUID shape used by `SmrtCollection.get()` to decide whether a\n * string filter is an id or a slug.\n */\nconst GET_STRING_FILTER_UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Resolve a string filter to the WHERE shape `SmrtCollection.get()` gives it:\n * a UUID string becomes `{ id }`, anything else becomes the natural-key lookup\n * `{ slug, context: '' }`.\n *\n * This is the single source of truth for that string-filter contract. Any\n * `beforeGet` interceptor that rewrites a string filter into an object filter\n * MUST resolve it through this helper first — the tenancy interceptor used to\n * rewrite every string to `{ id: filter, tenantId }`, which broke get-by-slug\n * under an active tenant context (null on SQLite, a uuid cast error on\n * PostgreSQL native-uuid id columns). See #2365.\n *\n * @param filter - The string filter passed to `get()`\n * @returns The object filter `get()` would build for that string\n */\nexport function resolveGetStringFilter(\n filter: string,\n): { id: string } | { slug: string; context: string } {\n return GET_STRING_FILTER_UUID_PATTERN.test(filter)\n ? { id: filter }\n : { slug: filter, context: '' };\n}\n\n/**\n * Helper function to create an interceptor context\n * @internal\n */\nexport function createInterceptorContext(\n className: string,\n operation: InterceptorContext['operation'],\n collectionName?: string,\n metadata?: Record<string, unknown>,\n): InterceptorContext {\n return {\n className,\n collectionName,\n operation,\n timestamp: new Date(),\n metadata,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/learning/memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/learning/memory.ts"],"names":[],"mappings":"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"}
|
package/dist/learning/memory.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toSafeInteger } from "../utils/safe-integer.js";
|
|
1
2
|
//#region src/learning/memory.ts
|
|
2
3
|
/** Default reinforcement thresholds. */
|
|
3
4
|
var DEFAULT_LEARNING_CONFIG = {
|
|
@@ -161,8 +162,8 @@ var LearningMemory = class {
|
|
|
161
162
|
if (existing) {
|
|
162
163
|
const currentConfidence = Number(existing.confidence ?? 1);
|
|
163
164
|
const nextConfidence = this.nextConfidence(currentConfidence, success);
|
|
164
|
-
const successCount =
|
|
165
|
-
const failureCount =
|
|
165
|
+
const successCount = toSafeInteger(toSafeInteger(existing.success_count ?? 0, "Learning memory success count") + (success ? 1 : 0), "Learning memory success count");
|
|
166
|
+
const failureCount = toSafeInteger(toSafeInteger(existing.failure_count ?? 0, "Learning memory failure count") + (success ? 0 : 1), "Learning memory failure count");
|
|
166
167
|
const data = {
|
|
167
168
|
confidence: nextConfidence,
|
|
168
169
|
success_count: successCount,
|
|
@@ -275,8 +276,8 @@ var LearningMemory = class {
|
|
|
275
276
|
key: String(row.key),
|
|
276
277
|
value: parseValue(row.value),
|
|
277
278
|
confidence: effective,
|
|
278
|
-
successCount:
|
|
279
|
-
failureCount:
|
|
279
|
+
successCount: toSafeInteger(row.success_count ?? 0, "Learning memory success count"),
|
|
280
|
+
failureCount: toSafeInteger(row.failure_count ?? 0, "Learning memory failure count"),
|
|
280
281
|
lastUsedAt,
|
|
281
282
|
source: "context"
|
|
282
283
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.js","names":[],"sources":["../../src/learning/memory.ts"],"sourcesContent":["/**\n * LearningMemory — confidence-scored, self-correcting memory over the existing\n * `_smrt_contexts` (keyed recall) and `_smrt_embeddings` (semantic recall)\n * substrate.\n *\n * This is the L1 deep module of the tenant-learning-agents epic (#1885 / #1886).\n * It wires the reinforcement columns that ship on `_smrt_contexts` but were\n * never written — `success_count`, `failure_count`, and a `last_used_at` that\n * is refreshed on recall — and layers a confidence floor + decay on top of the\n * primitive `remember()` / `recall()` methods so a strategy that stops working\n * stops being reused.\n *\n * It generalises the confidence-scored parser cache anytown's `praeco` agent\n * hand-rolls: recall only reuses a memory at `confidence >= minConfidence`\n * (default 0.7); a validated success strengthens confidence toward 1.0; an\n * observed failure decays it toward `failureConfidence` (default 0.3) so a\n * single failure drops it below the reuse floor.\n *\n * @module\n */\n\nimport type { DatabaseInterface } from '@happyvertical/sql';\n\n/**\n * A relevance-scored memory returned by {@link LearningMemory.recall}.\n *\n * Records sourced from keyed `_smrt_contexts` lookups carry reinforcement\n * counters; records sourced from semantic embedding search carry a\n * `similarity` score (mirrored into `confidence` for uniform ranking).\n */\nexport interface LearningMemoryRecord {\n /** Row id in `_smrt_contexts`, or the matched object id for semantic hits. */\n id: string;\n /** Hierarchical scope the memory is filed under. */\n scope: string;\n /** Lookup key within the scope. */\n key: string;\n /** The stored value (JSON-parsed for context rows; the matched object for semantic hits). */\n value: unknown;\n /** Confidence in `[0, 1]`. For semantic hits this equals `similarity`. */\n confidence: number;\n /** Number of captured successes reinforcing this memory. */\n successCount: number;\n /** Number of captured failures decaying this memory. */\n failureCount: number;\n /** Last time this memory was recalled or reinforced. */\n lastUsedAt: Date | null;\n /** Where the record came from. */\n source: 'context' | 'semantic';\n /** Cosine similarity for semantic hits (absent for context rows). */\n similarity?: number;\n}\n\n/**\n * The outcome of acting on a recalled (or freshly generated) memory.\n *\n * A boolean `success` signal is the common case; a numeric `metric` delta is\n * supported as a convenience (positive → success, non-positive → failure).\n */\nexport type LearningOutcome =\n | { success: boolean; error?: string }\n | { metric: number; error?: string };\n\n/**\n * The episode being reinforced — the scope/key of the memory the agent acted\n * on, plus (for a first capture) the value to persist.\n */\nexport interface LearningEpisode {\n /** Scope the memory is filed under (e.g. `'sample/parse-invoice'`). */\n scope: string;\n /** Lookup key within the scope. */\n key: string;\n /**\n * The value (strategy) this outcome pertains to. Seeds a new memory when none\n * exists for `(scope, key)`, and refreshes the stored value when one does — so\n * a regenerated strategy supersedes a decayed one. Omit to reinforce an\n * existing memory's confidence without touching its stored value.\n */\n value?: unknown;\n /** Optional metadata to persist on a first capture. */\n metadata?: Record<string, unknown>;\n /** Optional expiry after which recall ignores the memory. */\n expiresAt?: Date;\n}\n\n/**\n * Tunable thresholds for the reinforcement loop. Defaults mirror the proven\n * `praeco` behaviour.\n */\nexport interface LearningMemoryConfig {\n /** Reuse floor — recall omits memories below this confidence. Default 0.7. */\n minConfidence: number;\n /** Confidence a brand-new memory is seeded at on a first success. Default 0.9. */\n successConfidence: number;\n /** Target a memory decays toward on failure. Default 0.3. */\n failureConfidence: number;\n /**\n * Blend weight in `[0, 1]` for each reinforcement step. At 0.5 a single\n * failure from any confident value (>= 0.7) always lands below the 0.7 floor\n * (`0.5·c + 0.15 <= 0.65`). Default 0.5.\n */\n reinforcement: number;\n /**\n * Optional half-life (ms) for time-based confidence decay. When set, recall\n * discounts a memory's stored confidence by `0.5 ^ (age / halfLife)` based on\n * `last_used_at`, so stale memory falls below the floor over time even\n * without an explicit failure. Off (no time decay) when undefined.\n */\n decayHalfLifeMs?: number;\n}\n\n/** Default reinforcement thresholds. */\nexport const DEFAULT_LEARNING_CONFIG: LearningMemoryConfig = {\n minConfidence: 0.7,\n successConfidence: 0.9,\n failureConfidence: 0.3,\n reinforcement: 0.5,\n};\n\n/**\n * A semantic search over stored embeddings, injected so `LearningMemory` never\n * reaches into a collection's internals. Matches the shape of\n * `SmrtCollection.semanticSearch`.\n */\nexport type LearningSemanticSearch = (\n query: string,\n options: {\n limit?: number;\n minSimilarity?: number;\n where?: Record<string, unknown>;\n },\n) => Promise<\n Array<{ id?: string; _similarity: number } & Record<string, unknown>>\n>;\n\n/** Options for {@link LearningMemory.recall}. */\nexport interface LearningRecallOptions {\n /** Exact key to look up within the scope. Omit for a scope-wide recall. */\n key?: string;\n /** Free-text query for semantic recall (only used when a searcher is wired). */\n query?: string;\n /** Override the reuse floor for this call. */\n minConfidence?: number;\n /** Walk up the scope hierarchy when no match is found at `scope`. Default true. */\n includeAncestors?: boolean;\n /** Cap on returned records. */\n limit?: number;\n /** Cap on semantic hits requested from the searcher. Default 10. */\n semanticLimit?: number;\n /** Minimum cosine similarity for semantic hits. Default 0. */\n minSimilarity?: number;\n /** Reference time for decay/expiry evaluation (injectable for tests). */\n now?: Date;\n}\n\n/** Constructor options for {@link LearningMemory}. */\nexport interface LearningMemoryOptions {\n /** System database handle (an owning object's `systemDb`). */\n db: DatabaseInterface;\n /** `owner_class` the memories are filed under. */\n ownerClass: string;\n /** `owner_id` the memories are filed under (isolates memory per owner). */\n ownerId: string;\n /**\n * Optional tenant id. When set it is threaded into the semantic searcher's\n * `where` filter so embedding recall stays tenant-scoped. Keyed-context\n * recall is already isolated by `owner_id`.\n */\n tenantId?: string | null;\n /** Optional embedding search for the semantic recall arm. */\n semanticSearch?: LearningSemanticSearch;\n /** Threshold overrides; merged over {@link DEFAULT_LEARNING_CONFIG}. */\n config?: Partial<LearningMemoryConfig>;\n}\n\nconst CONTEXTS_TABLE = '_smrt_contexts';\nconst CONFLICT_COLUMNS = ['owner_class', 'owner_id', 'scope', 'key', 'version'];\n/**\n * The single `_smrt_contexts` version LearningMemory reads and writes. Kept\n * explicit so capture and recall agree on the row identity.\n */\nconst MEMORY_VERSION = 1;\n\nfunction clamp01(value: number): number {\n if (Number.isNaN(value)) return 0;\n if (value < 0) return 0;\n if (value > 1) return 1;\n return value;\n}\n\nfunction normalizeOutcome(outcome: LearningOutcome): {\n success: boolean;\n error?: string;\n} {\n if ('success' in outcome) {\n return { success: outcome.success, error: outcome.error };\n }\n return { success: outcome.metric > 0, error: outcome.error };\n}\n\nfunction toDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return value;\n const parsed = new Date(value as string);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n}\n\nfunction parseValue(raw: unknown): unknown {\n if (typeof raw !== 'string') return raw ?? null;\n try {\n return JSON.parse(raw);\n } catch {\n // Tolerate legacy/non-JSON payloads rather than throwing mid-recall.\n return raw;\n }\n}\n\n/**\n * Confidence-scored, self-correcting memory over `_smrt_contexts` and\n * (optionally) semantic embedding search.\n *\n * @example\n * ```typescript\n * const memory = new LearningMemory({\n * db: agent.systemDb,\n * ownerClass: 'InvoiceAgent',\n * ownerId: agent.id,\n * tenantId: agent.tenantId,\n * });\n *\n * // Recall a confident strategy for this task.\n * const [hit] = await memory.recall('parser/acme', { key: docUrl });\n * const strategy = hit?.value ?? (await generateStrategy(docUrl));\n *\n * // ...act on `strategy`, then reinforce the outcome.\n * await memory.capture(\n * { scope: 'parser/acme', key: docUrl, value: strategy },\n * { success: extracted.length > 0 },\n * );\n * ```\n */\nexport class LearningMemory {\n private readonly db: DatabaseInterface;\n private readonly ownerClass: string;\n private readonly ownerId: string;\n private readonly tenantId: string | null;\n private readonly semanticSearch?: LearningSemanticSearch;\n private readonly config: LearningMemoryConfig;\n\n constructor(options: LearningMemoryOptions) {\n this.db = options.db;\n this.ownerClass = options.ownerClass;\n this.ownerId = options.ownerId;\n this.tenantId = options.tenantId ?? null;\n this.semanticSearch = options.semanticSearch;\n this.config = { ...DEFAULT_LEARNING_CONFIG, ...(options.config ?? {}) };\n }\n\n /** The resolved reinforcement thresholds (defaults merged with overrides). */\n get thresholds(): LearningMemoryConfig {\n return { ...this.config };\n }\n\n /**\n * Recall confidence-filtered, relevant memories for a scope.\n *\n * Unions two arms:\n * 1. **Keyed context recall** over `_smrt_contexts` — owner-scoped (and thus\n * tenant-isolated), filtered by the confidence floor, expiry, and optional\n * time-decay, with hierarchical scope fallback. Surviving rows have their\n * `last_used_at` refreshed.\n * 2. **Semantic recall** over stored embeddings (only when a searcher was\n * wired and a `query` is given) — tenant-scoped via the searcher's `where`.\n *\n * @returns Records sorted by confidence (then similarity) descending.\n */\n async recall(\n scope: string,\n options: LearningRecallOptions = {},\n ): Promise<LearningMemoryRecord[]> {\n const floor = options.minConfidence ?? this.config.minConfidence;\n const now = options.now ?? new Date();\n const includeAncestors = options.includeAncestors ?? true;\n\n const contextRecords = await this.recallContexts(scope, {\n key: options.key,\n floor,\n now,\n includeAncestors,\n });\n\n // Wire the previously-dead refresh: recall marks a memory as used.\n await this.touch(\n contextRecords.map((r) => r.id),\n now,\n );\n\n let semanticRecords: LearningMemoryRecord[] = [];\n if (options.query && this.semanticSearch) {\n // Semantic hits carry `confidence = _similarity`, so the reuse floor must\n // apply to them too: default `minSimilarity` to the resolved confidence\n // floor unless the caller explicitly overrides it. Otherwise a\n // low-similarity hit could be reused while a context memory at the same\n // confidence is filtered out.\n semanticRecords = await this.recallSemantic(options.query, {\n floor: options.minSimilarity ?? floor,\n limit: options.semanticLimit ?? 10,\n });\n }\n\n const merged = [...contextRecords, ...semanticRecords].sort((a, b) => {\n if (b.confidence !== a.confidence) return b.confidence - a.confidence;\n return (b.similarity ?? 0) - (a.similarity ?? 0);\n });\n\n return options.limit != null ? merged.slice(0, options.limit) : merged;\n }\n\n /**\n * Reinforce a memory from an observed outcome.\n *\n * - Wires the dormant reinforcement columns: increments `success_count` on\n * success, `failure_count` on failure.\n * - Adjusts `confidence`: success strengthens toward 1.0; failure decays\n * toward `failureConfidence` (default 0.3), dropping a confident memory\n * below the reuse floor in a single step.\n * - Refreshes `last_used_at` and `updated_at`, and (when the episode carries a\n * `value`) the stored value — so a regenerated strategy supersedes a decayed\n * one instead of the old value resurfacing on the next success.\n * - Seeds a new memory when none exists for `(scope, key)` and the episode\n * carries a `value` (a failed first attempt is retained at low confidence\n * for self-correction context).\n *\n * @returns The updated (or seeded) record, or `null` when there was nothing\n * to reinforce (no existing memory and no value to seed).\n */\n async capture(\n episode: LearningEpisode,\n outcome: LearningOutcome,\n ): Promise<LearningMemoryRecord | null> {\n const { success, error } = normalizeOutcome(outcome);\n const now = new Date();\n\n const existing = await this.db.get(CONTEXTS_TABLE, {\n owner_class: this.ownerClass,\n owner_id: this.ownerId,\n scope: episode.scope,\n key: episode.key,\n version: MEMORY_VERSION,\n });\n\n if (existing) {\n const currentConfidence = Number(existing.confidence ?? 1);\n const nextConfidence = this.nextConfidence(currentConfidence, success);\n const successCount =\n Number(existing.success_count ?? 0) + (success ? 1 : 0);\n const failureCount =\n Number(existing.failure_count ?? 0) + (success ? 0 : 1);\n\n const data: Record<string, unknown> = {\n confidence: nextConfidence,\n success_count: successCount,\n failure_count: failureCount,\n last_used_at: now,\n updated_at: now,\n };\n // The episode's value is the strategy this outcome pertains to, so always\n // reflect it in storage. Without this, a regenerated strategy (staged\n // after the previous one decayed below the floor) would be discarded and\n // the next successful capture would resurrect the stale failed value.\n if (episode.value !== undefined) {\n data.value = JSON.stringify(episode.value);\n }\n if (episode.metadata !== undefined) {\n data.metadata = JSON.stringify({\n ...this.parseMetadata(existing.metadata),\n ...episode.metadata,\n ...(error ? { lastError: error } : {}),\n });\n } else if (error) {\n data.metadata = JSON.stringify({\n ...this.parseMetadata(existing.metadata),\n lastError: error,\n });\n }\n\n await this.db.update(CONTEXTS_TABLE, { id: existing.id }, data);\n\n return {\n id: String(existing.id),\n scope: episode.scope,\n key: episode.key,\n value:\n episode.value !== undefined\n ? episode.value\n : parseValue(existing.value),\n confidence: nextConfidence,\n successCount,\n failureCount,\n lastUsedAt: now,\n source: 'context',\n };\n }\n\n // No existing memory: seed one only if the episode provides a value.\n if (episode.value === undefined) {\n return null;\n }\n\n const id = crypto.randomUUID();\n const confidence = success\n ? this.config.successConfidence\n : this.config.failureConfidence;\n const metadata =\n episode.metadata || error\n ? JSON.stringify({\n ...(episode.metadata ?? {}),\n ...(error ? { lastError: error } : {}),\n })\n : null;\n\n await this.db.upsert(CONTEXTS_TABLE, CONFLICT_COLUMNS, {\n id,\n owner_class: this.ownerClass,\n owner_id: this.ownerId,\n scope: episode.scope,\n key: episode.key,\n value: JSON.stringify(episode.value),\n metadata,\n version: MEMORY_VERSION,\n confidence,\n success_count: success ? 1 : 0,\n failure_count: success ? 0 : 1,\n created_at: now,\n updated_at: now,\n last_used_at: now,\n expires_at: episode.expiresAt ?? null,\n });\n\n return {\n id,\n scope: episode.scope,\n key: episode.key,\n value: episode.value,\n confidence,\n successCount: success ? 1 : 0,\n failureCount: success ? 0 : 1,\n lastUsedAt: now,\n source: 'context',\n };\n }\n\n /**\n * Compute the next confidence from the current value and an outcome.\n *\n * Success strengthens toward 1.0; failure blends toward `failureConfidence`.\n * Exposed as a pure step so callers/tests can reason about the curve.\n */\n nextConfidence(current: number, success: boolean): number {\n const r = this.config.reinforcement;\n if (success) {\n return clamp01(current + r * (1 - current));\n }\n return clamp01(current + r * (this.config.failureConfidence - current));\n }\n\n private async recallContexts(\n scope: string,\n opts: {\n key?: string;\n floor: number;\n now: Date;\n includeAncestors: boolean;\n },\n ): Promise<LearningMemoryRecord[]> {\n const scopes = opts.includeAncestors ? this.scopeChain(scope) : [scope];\n\n for (const candidateScope of scopes) {\n const rows = await this.selectContextRows(candidateScope, opts.key);\n const records = this.filterAndRank(rows, opts.floor, opts.now);\n if (records.length > 0) {\n return records;\n }\n }\n return [];\n }\n\n private async selectContextRows(\n scope: string,\n key?: string,\n ): Promise<Record<string, unknown>[]> {\n // Row identity is (owner_class, owner_id, scope, key, version); `capture()`\n // only ever writes version 1, so recall filters to it — otherwise a future\n // multi-version write would surface stale versions here.\n if (key !== undefined) {\n const { rows } = await this.db.query(\n `SELECT * FROM ${CONTEXTS_TABLE}\n WHERE owner_class = ? AND owner_id = ? AND scope = ? AND key = ?\n AND version = ?`,\n this.ownerClass,\n this.ownerId,\n scope,\n key,\n MEMORY_VERSION,\n );\n return rows;\n }\n const { rows } = await this.db.query(\n `SELECT * FROM ${CONTEXTS_TABLE}\n WHERE owner_class = ? AND owner_id = ? AND scope = ? AND version = ?`,\n this.ownerClass,\n this.ownerId,\n scope,\n MEMORY_VERSION,\n );\n return rows;\n }\n\n private filterAndRank(\n rows: Record<string, unknown>[],\n floor: number,\n now: Date,\n ): LearningMemoryRecord[] {\n const records: LearningMemoryRecord[] = [];\n\n for (const row of rows) {\n const expiresAt = toDate(row.expires_at);\n if (expiresAt && expiresAt.getTime() <= now.getTime()) {\n continue;\n }\n\n const lastUsedAt = toDate(row.last_used_at);\n const stored = Number(row.confidence ?? 1);\n const effective = this.applyTimeDecay(stored, lastUsedAt, now);\n if (effective < floor) {\n continue;\n }\n\n records.push({\n id: String(row.id),\n scope: String(row.scope),\n key: String(row.key),\n value: parseValue(row.value),\n confidence: effective,\n successCount: Number(row.success_count ?? 0),\n failureCount: Number(row.failure_count ?? 0),\n lastUsedAt,\n source: 'context',\n });\n }\n\n // Highest effective confidence first.\n return records.sort((a, b) => b.confidence - a.confidence);\n }\n\n private applyTimeDecay(\n confidence: number,\n lastUsedAt: Date | null,\n now: Date,\n ): number {\n const halfLife = this.config.decayHalfLifeMs;\n if (!halfLife || halfLife <= 0 || !lastUsedAt) {\n return confidence;\n }\n const age = now.getTime() - lastUsedAt.getTime();\n if (age <= 0) return confidence;\n return clamp01(confidence * 0.5 ** (age / halfLife));\n }\n\n private async recallSemantic(\n query: string,\n opts: { floor: number; limit: number },\n ): Promise<LearningMemoryRecord[]> {\n if (!this.semanticSearch) return [];\n\n const where: Record<string, unknown> | undefined =\n this.tenantId != null ? { tenant_id: this.tenantId } : undefined;\n\n const hits = await this.semanticSearch(query, {\n limit: opts.limit,\n minSimilarity: opts.floor,\n where,\n });\n\n return hits.map((hit) => ({\n id: hit.id != null ? String(hit.id) : crypto.randomUUID(),\n scope: 'semantic',\n key: hit.id != null ? String(hit.id) : query,\n value: hit,\n confidence: hit._similarity,\n successCount: 0,\n failureCount: 0,\n lastUsedAt: null,\n source: 'semantic',\n similarity: hit._similarity,\n }));\n }\n\n private async touch(ids: string[], now: Date): Promise<void> {\n if (ids.length === 0) return;\n // Single batched UPDATE rather than one per row (avoids an N+1 on large\n // recalls). Raw parameterised queries don't coerce Date bind params the way\n // db.update() does, so serialise to ISO first.\n const placeholders = ids.map(() => '?').join(', ');\n await this.db.query(\n `UPDATE ${CONTEXTS_TABLE} SET last_used_at = ? WHERE id IN (${placeholders})`,\n now.toISOString(),\n ...ids,\n );\n }\n\n private scopeChain(scope: string): string[] {\n const chain: string[] = [scope];\n const parts = scope.split('/');\n while (parts.length > 0) {\n parts.pop();\n chain.push(parts.join('/') || 'global');\n }\n // De-dupe while preserving order (guards against a leading 'global').\n return [...new Set(chain)];\n }\n\n private parseMetadata(raw: unknown): Record<string, unknown> {\n if (typeof raw !== 'string' || raw.length === 0) return {};\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"memory.js","names":[],"sources":["../../src/learning/memory.ts"],"sourcesContent":["/**\n * LearningMemory — confidence-scored, self-correcting memory over the existing\n * `_smrt_contexts` (keyed recall) and `_smrt_embeddings` (semantic recall)\n * substrate.\n *\n * This is the L1 deep module of the tenant-learning-agents epic (#1885 / #1886).\n * It wires the reinforcement columns that ship on `_smrt_contexts` but were\n * never written — `success_count`, `failure_count`, and a `last_used_at` that\n * is refreshed on recall — and layers a confidence floor + decay on top of the\n * primitive `remember()` / `recall()` methods so a strategy that stops working\n * stops being reused.\n *\n * It generalises the confidence-scored parser cache anytown's `praeco` agent\n * hand-rolls: recall only reuses a memory at `confidence >= minConfidence`\n * (default 0.7); a validated success strengthens confidence toward 1.0; an\n * observed failure decays it toward `failureConfidence` (default 0.3) so a\n * single failure drops it below the reuse floor.\n *\n * @module\n */\n\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { toSafeInteger } from '../utils/safe-integer.js';\n\n/**\n * A relevance-scored memory returned by {@link LearningMemory.recall}.\n *\n * Records sourced from keyed `_smrt_contexts` lookups carry reinforcement\n * counters; records sourced from semantic embedding search carry a\n * `similarity` score (mirrored into `confidence` for uniform ranking).\n */\nexport interface LearningMemoryRecord {\n /** Row id in `_smrt_contexts`, or the matched object id for semantic hits. */\n id: string;\n /** Hierarchical scope the memory is filed under. */\n scope: string;\n /** Lookup key within the scope. */\n key: string;\n /** The stored value (JSON-parsed for context rows; the matched object for semantic hits). */\n value: unknown;\n /** Confidence in `[0, 1]`. For semantic hits this equals `similarity`. */\n confidence: number;\n /** Number of captured successes reinforcing this memory. */\n successCount: number;\n /** Number of captured failures decaying this memory. */\n failureCount: number;\n /** Last time this memory was recalled or reinforced. */\n lastUsedAt: Date | null;\n /** Where the record came from. */\n source: 'context' | 'semantic';\n /** Cosine similarity for semantic hits (absent for context rows). */\n similarity?: number;\n}\n\n/**\n * The outcome of acting on a recalled (or freshly generated) memory.\n *\n * A boolean `success` signal is the common case; a numeric `metric` delta is\n * supported as a convenience (positive → success, non-positive → failure).\n */\nexport type LearningOutcome =\n | { success: boolean; error?: string }\n | { metric: number; error?: string };\n\n/**\n * The episode being reinforced — the scope/key of the memory the agent acted\n * on, plus (for a first capture) the value to persist.\n */\nexport interface LearningEpisode {\n /** Scope the memory is filed under (e.g. `'sample/parse-invoice'`). */\n scope: string;\n /** Lookup key within the scope. */\n key: string;\n /**\n * The value (strategy) this outcome pertains to. Seeds a new memory when none\n * exists for `(scope, key)`, and refreshes the stored value when one does — so\n * a regenerated strategy supersedes a decayed one. Omit to reinforce an\n * existing memory's confidence without touching its stored value.\n */\n value?: unknown;\n /** Optional metadata to persist on a first capture. */\n metadata?: Record<string, unknown>;\n /** Optional expiry after which recall ignores the memory. */\n expiresAt?: Date;\n}\n\n/**\n * Tunable thresholds for the reinforcement loop. Defaults mirror the proven\n * `praeco` behaviour.\n */\nexport interface LearningMemoryConfig {\n /** Reuse floor — recall omits memories below this confidence. Default 0.7. */\n minConfidence: number;\n /** Confidence a brand-new memory is seeded at on a first success. Default 0.9. */\n successConfidence: number;\n /** Target a memory decays toward on failure. Default 0.3. */\n failureConfidence: number;\n /**\n * Blend weight in `[0, 1]` for each reinforcement step. At 0.5 a single\n * failure from any confident value (>= 0.7) always lands below the 0.7 floor\n * (`0.5·c + 0.15 <= 0.65`). Default 0.5.\n */\n reinforcement: number;\n /**\n * Optional half-life (ms) for time-based confidence decay. When set, recall\n * discounts a memory's stored confidence by `0.5 ^ (age / halfLife)` based on\n * `last_used_at`, so stale memory falls below the floor over time even\n * without an explicit failure. Off (no time decay) when undefined.\n */\n decayHalfLifeMs?: number;\n}\n\n/** Default reinforcement thresholds. */\nexport const DEFAULT_LEARNING_CONFIG: LearningMemoryConfig = {\n minConfidence: 0.7,\n successConfidence: 0.9,\n failureConfidence: 0.3,\n reinforcement: 0.5,\n};\n\n/**\n * A semantic search over stored embeddings, injected so `LearningMemory` never\n * reaches into a collection's internals. Matches the shape of\n * `SmrtCollection.semanticSearch`.\n */\nexport type LearningSemanticSearch = (\n query: string,\n options: {\n limit?: number;\n minSimilarity?: number;\n where?: Record<string, unknown>;\n },\n) => Promise<\n Array<{ id?: string; _similarity: number } & Record<string, unknown>>\n>;\n\n/** Options for {@link LearningMemory.recall}. */\nexport interface LearningRecallOptions {\n /** Exact key to look up within the scope. Omit for a scope-wide recall. */\n key?: string;\n /** Free-text query for semantic recall (only used when a searcher is wired). */\n query?: string;\n /** Override the reuse floor for this call. */\n minConfidence?: number;\n /** Walk up the scope hierarchy when no match is found at `scope`. Default true. */\n includeAncestors?: boolean;\n /** Cap on returned records. */\n limit?: number;\n /** Cap on semantic hits requested from the searcher. Default 10. */\n semanticLimit?: number;\n /** Minimum cosine similarity for semantic hits. Default 0. */\n minSimilarity?: number;\n /** Reference time for decay/expiry evaluation (injectable for tests). */\n now?: Date;\n}\n\n/** Constructor options for {@link LearningMemory}. */\nexport interface LearningMemoryOptions {\n /** System database handle (an owning object's `systemDb`). */\n db: DatabaseInterface;\n /** `owner_class` the memories are filed under. */\n ownerClass: string;\n /** `owner_id` the memories are filed under (isolates memory per owner). */\n ownerId: string;\n /**\n * Optional tenant id. When set it is threaded into the semantic searcher's\n * `where` filter so embedding recall stays tenant-scoped. Keyed-context\n * recall is already isolated by `owner_id`.\n */\n tenantId?: string | null;\n /** Optional embedding search for the semantic recall arm. */\n semanticSearch?: LearningSemanticSearch;\n /** Threshold overrides; merged over {@link DEFAULT_LEARNING_CONFIG}. */\n config?: Partial<LearningMemoryConfig>;\n}\n\nconst CONTEXTS_TABLE = '_smrt_contexts';\nconst CONFLICT_COLUMNS = ['owner_class', 'owner_id', 'scope', 'key', 'version'];\n/**\n * The single `_smrt_contexts` version LearningMemory reads and writes. Kept\n * explicit so capture and recall agree on the row identity.\n */\nconst MEMORY_VERSION = 1;\n\nfunction clamp01(value: number): number {\n if (Number.isNaN(value)) return 0;\n if (value < 0) return 0;\n if (value > 1) return 1;\n return value;\n}\n\nfunction normalizeOutcome(outcome: LearningOutcome): {\n success: boolean;\n error?: string;\n} {\n if ('success' in outcome) {\n return { success: outcome.success, error: outcome.error };\n }\n return { success: outcome.metric > 0, error: outcome.error };\n}\n\nfunction toDate(value: unknown): Date | null {\n if (value == null) return null;\n if (value instanceof Date) return value;\n const parsed = new Date(value as string);\n return Number.isNaN(parsed.getTime()) ? null : parsed;\n}\n\nfunction parseValue(raw: unknown): unknown {\n if (typeof raw !== 'string') return raw ?? null;\n try {\n return JSON.parse(raw);\n } catch {\n // Tolerate legacy/non-JSON payloads rather than throwing mid-recall.\n return raw;\n }\n}\n\n/**\n * Confidence-scored, self-correcting memory over `_smrt_contexts` and\n * (optionally) semantic embedding search.\n *\n * @example\n * ```typescript\n * const memory = new LearningMemory({\n * db: agent.systemDb,\n * ownerClass: 'InvoiceAgent',\n * ownerId: agent.id,\n * tenantId: agent.tenantId,\n * });\n *\n * // Recall a confident strategy for this task.\n * const [hit] = await memory.recall('parser/acme', { key: docUrl });\n * const strategy = hit?.value ?? (await generateStrategy(docUrl));\n *\n * // ...act on `strategy`, then reinforce the outcome.\n * await memory.capture(\n * { scope: 'parser/acme', key: docUrl, value: strategy },\n * { success: extracted.length > 0 },\n * );\n * ```\n */\nexport class LearningMemory {\n private readonly db: DatabaseInterface;\n private readonly ownerClass: string;\n private readonly ownerId: string;\n private readonly tenantId: string | null;\n private readonly semanticSearch?: LearningSemanticSearch;\n private readonly config: LearningMemoryConfig;\n\n constructor(options: LearningMemoryOptions) {\n this.db = options.db;\n this.ownerClass = options.ownerClass;\n this.ownerId = options.ownerId;\n this.tenantId = options.tenantId ?? null;\n this.semanticSearch = options.semanticSearch;\n this.config = { ...DEFAULT_LEARNING_CONFIG, ...(options.config ?? {}) };\n }\n\n /** The resolved reinforcement thresholds (defaults merged with overrides). */\n get thresholds(): LearningMemoryConfig {\n return { ...this.config };\n }\n\n /**\n * Recall confidence-filtered, relevant memories for a scope.\n *\n * Unions two arms:\n * 1. **Keyed context recall** over `_smrt_contexts` — owner-scoped (and thus\n * tenant-isolated), filtered by the confidence floor, expiry, and optional\n * time-decay, with hierarchical scope fallback. Surviving rows have their\n * `last_used_at` refreshed.\n * 2. **Semantic recall** over stored embeddings (only when a searcher was\n * wired and a `query` is given) — tenant-scoped via the searcher's `where`.\n *\n * @returns Records sorted by confidence (then similarity) descending.\n */\n async recall(\n scope: string,\n options: LearningRecallOptions = {},\n ): Promise<LearningMemoryRecord[]> {\n const floor = options.minConfidence ?? this.config.minConfidence;\n const now = options.now ?? new Date();\n const includeAncestors = options.includeAncestors ?? true;\n\n const contextRecords = await this.recallContexts(scope, {\n key: options.key,\n floor,\n now,\n includeAncestors,\n });\n\n // Wire the previously-dead refresh: recall marks a memory as used.\n await this.touch(\n contextRecords.map((r) => r.id),\n now,\n );\n\n let semanticRecords: LearningMemoryRecord[] = [];\n if (options.query && this.semanticSearch) {\n // Semantic hits carry `confidence = _similarity`, so the reuse floor must\n // apply to them too: default `minSimilarity` to the resolved confidence\n // floor unless the caller explicitly overrides it. Otherwise a\n // low-similarity hit could be reused while a context memory at the same\n // confidence is filtered out.\n semanticRecords = await this.recallSemantic(options.query, {\n floor: options.minSimilarity ?? floor,\n limit: options.semanticLimit ?? 10,\n });\n }\n\n const merged = [...contextRecords, ...semanticRecords].sort((a, b) => {\n if (b.confidence !== a.confidence) return b.confidence - a.confidence;\n return (b.similarity ?? 0) - (a.similarity ?? 0);\n });\n\n return options.limit != null ? merged.slice(0, options.limit) : merged;\n }\n\n /**\n * Reinforce a memory from an observed outcome.\n *\n * - Wires the dormant reinforcement columns: increments `success_count` on\n * success, `failure_count` on failure.\n * - Adjusts `confidence`: success strengthens toward 1.0; failure decays\n * toward `failureConfidence` (default 0.3), dropping a confident memory\n * below the reuse floor in a single step.\n * - Refreshes `last_used_at` and `updated_at`, and (when the episode carries a\n * `value`) the stored value — so a regenerated strategy supersedes a decayed\n * one instead of the old value resurfacing on the next success.\n * - Seeds a new memory when none exists for `(scope, key)` and the episode\n * carries a `value` (a failed first attempt is retained at low confidence\n * for self-correction context).\n *\n * @returns The updated (or seeded) record, or `null` when there was nothing\n * to reinforce (no existing memory and no value to seed).\n */\n async capture(\n episode: LearningEpisode,\n outcome: LearningOutcome,\n ): Promise<LearningMemoryRecord | null> {\n const { success, error } = normalizeOutcome(outcome);\n const now = new Date();\n\n const existing = await this.db.get(CONTEXTS_TABLE, {\n owner_class: this.ownerClass,\n owner_id: this.ownerId,\n scope: episode.scope,\n key: episode.key,\n version: MEMORY_VERSION,\n });\n\n if (existing) {\n const currentConfidence = Number(existing.confidence ?? 1);\n const nextConfidence = this.nextConfidence(currentConfidence, success);\n const successCount = toSafeInteger(\n toSafeInteger(\n existing.success_count ?? 0,\n 'Learning memory success count',\n ) + (success ? 1 : 0),\n 'Learning memory success count',\n );\n const failureCount = toSafeInteger(\n toSafeInteger(\n existing.failure_count ?? 0,\n 'Learning memory failure count',\n ) + (success ? 0 : 1),\n 'Learning memory failure count',\n );\n\n const data: Record<string, unknown> = {\n confidence: nextConfidence,\n success_count: successCount,\n failure_count: failureCount,\n last_used_at: now,\n updated_at: now,\n };\n // The episode's value is the strategy this outcome pertains to, so always\n // reflect it in storage. Without this, a regenerated strategy (staged\n // after the previous one decayed below the floor) would be discarded and\n // the next successful capture would resurrect the stale failed value.\n if (episode.value !== undefined) {\n data.value = JSON.stringify(episode.value);\n }\n if (episode.metadata !== undefined) {\n data.metadata = JSON.stringify({\n ...this.parseMetadata(existing.metadata),\n ...episode.metadata,\n ...(error ? { lastError: error } : {}),\n });\n } else if (error) {\n data.metadata = JSON.stringify({\n ...this.parseMetadata(existing.metadata),\n lastError: error,\n });\n }\n\n await this.db.update(CONTEXTS_TABLE, { id: existing.id }, data);\n\n return {\n id: String(existing.id),\n scope: episode.scope,\n key: episode.key,\n value:\n episode.value !== undefined\n ? episode.value\n : parseValue(existing.value),\n confidence: nextConfidence,\n successCount,\n failureCount,\n lastUsedAt: now,\n source: 'context',\n };\n }\n\n // No existing memory: seed one only if the episode provides a value.\n if (episode.value === undefined) {\n return null;\n }\n\n const id = crypto.randomUUID();\n const confidence = success\n ? this.config.successConfidence\n : this.config.failureConfidence;\n const metadata =\n episode.metadata || error\n ? JSON.stringify({\n ...(episode.metadata ?? {}),\n ...(error ? { lastError: error } : {}),\n })\n : null;\n\n await this.db.upsert(CONTEXTS_TABLE, CONFLICT_COLUMNS, {\n id,\n owner_class: this.ownerClass,\n owner_id: this.ownerId,\n scope: episode.scope,\n key: episode.key,\n value: JSON.stringify(episode.value),\n metadata,\n version: MEMORY_VERSION,\n confidence,\n success_count: success ? 1 : 0,\n failure_count: success ? 0 : 1,\n created_at: now,\n updated_at: now,\n last_used_at: now,\n expires_at: episode.expiresAt ?? null,\n });\n\n return {\n id,\n scope: episode.scope,\n key: episode.key,\n value: episode.value,\n confidence,\n successCount: success ? 1 : 0,\n failureCount: success ? 0 : 1,\n lastUsedAt: now,\n source: 'context',\n };\n }\n\n /**\n * Compute the next confidence from the current value and an outcome.\n *\n * Success strengthens toward 1.0; failure blends toward `failureConfidence`.\n * Exposed as a pure step so callers/tests can reason about the curve.\n */\n nextConfidence(current: number, success: boolean): number {\n const r = this.config.reinforcement;\n if (success) {\n return clamp01(current + r * (1 - current));\n }\n return clamp01(current + r * (this.config.failureConfidence - current));\n }\n\n private async recallContexts(\n scope: string,\n opts: {\n key?: string;\n floor: number;\n now: Date;\n includeAncestors: boolean;\n },\n ): Promise<LearningMemoryRecord[]> {\n const scopes = opts.includeAncestors ? this.scopeChain(scope) : [scope];\n\n for (const candidateScope of scopes) {\n const rows = await this.selectContextRows(candidateScope, opts.key);\n const records = this.filterAndRank(rows, opts.floor, opts.now);\n if (records.length > 0) {\n return records;\n }\n }\n return [];\n }\n\n private async selectContextRows(\n scope: string,\n key?: string,\n ): Promise<Record<string, unknown>[]> {\n // Row identity is (owner_class, owner_id, scope, key, version); `capture()`\n // only ever writes version 1, so recall filters to it — otherwise a future\n // multi-version write would surface stale versions here.\n if (key !== undefined) {\n const { rows } = await this.db.query(\n `SELECT * FROM ${CONTEXTS_TABLE}\n WHERE owner_class = ? AND owner_id = ? AND scope = ? AND key = ?\n AND version = ?`,\n this.ownerClass,\n this.ownerId,\n scope,\n key,\n MEMORY_VERSION,\n );\n return rows;\n }\n const { rows } = await this.db.query(\n `SELECT * FROM ${CONTEXTS_TABLE}\n WHERE owner_class = ? AND owner_id = ? AND scope = ? AND version = ?`,\n this.ownerClass,\n this.ownerId,\n scope,\n MEMORY_VERSION,\n );\n return rows;\n }\n\n private filterAndRank(\n rows: Record<string, unknown>[],\n floor: number,\n now: Date,\n ): LearningMemoryRecord[] {\n const records: LearningMemoryRecord[] = [];\n\n for (const row of rows) {\n const expiresAt = toDate(row.expires_at);\n if (expiresAt && expiresAt.getTime() <= now.getTime()) {\n continue;\n }\n\n const lastUsedAt = toDate(row.last_used_at);\n const stored = Number(row.confidence ?? 1);\n const effective = this.applyTimeDecay(stored, lastUsedAt, now);\n if (effective < floor) {\n continue;\n }\n\n records.push({\n id: String(row.id),\n scope: String(row.scope),\n key: String(row.key),\n value: parseValue(row.value),\n confidence: effective,\n successCount: toSafeInteger(\n row.success_count ?? 0,\n 'Learning memory success count',\n ),\n failureCount: toSafeInteger(\n row.failure_count ?? 0,\n 'Learning memory failure count',\n ),\n lastUsedAt,\n source: 'context',\n });\n }\n\n // Highest effective confidence first.\n return records.sort((a, b) => b.confidence - a.confidence);\n }\n\n private applyTimeDecay(\n confidence: number,\n lastUsedAt: Date | null,\n now: Date,\n ): number {\n const halfLife = this.config.decayHalfLifeMs;\n if (!halfLife || halfLife <= 0 || !lastUsedAt) {\n return confidence;\n }\n const age = now.getTime() - lastUsedAt.getTime();\n if (age <= 0) return confidence;\n return clamp01(confidence * 0.5 ** (age / halfLife));\n }\n\n private async recallSemantic(\n query: string,\n opts: { floor: number; limit: number },\n ): Promise<LearningMemoryRecord[]> {\n if (!this.semanticSearch) return [];\n\n const where: Record<string, unknown> | undefined =\n this.tenantId != null ? { tenant_id: this.tenantId } : undefined;\n\n const hits = await this.semanticSearch(query, {\n limit: opts.limit,\n minSimilarity: opts.floor,\n where,\n });\n\n return hits.map((hit) => ({\n id: hit.id != null ? String(hit.id) : crypto.randomUUID(),\n scope: 'semantic',\n key: hit.id != null ? String(hit.id) : query,\n value: hit,\n confidence: hit._similarity,\n successCount: 0,\n failureCount: 0,\n lastUsedAt: null,\n source: 'semantic',\n similarity: hit._similarity,\n }));\n }\n\n private async touch(ids: string[], now: Date): Promise<void> {\n if (ids.length === 0) return;\n // Single batched UPDATE rather than one per row (avoids an N+1 on large\n // recalls). Raw parameterised queries don't coerce Date bind params the way\n // db.update() does, so serialise to ISO first.\n const placeholders = ids.map(() => '?').join(', ');\n await this.db.query(\n `UPDATE ${CONTEXTS_TABLE} SET last_used_at = ? WHERE id IN (${placeholders})`,\n now.toISOString(),\n ...ids,\n );\n }\n\n private scopeChain(scope: string): string[] {\n const chain: string[] = [scope];\n const parts = scope.split('/');\n while (parts.length > 0) {\n parts.pop();\n chain.push(parts.join('/') || 'global');\n }\n // De-dupe while preserving order (guards against a leading 'global').\n return [...new Set(chain)];\n }\n\n private parseMetadata(raw: unknown): Record<string, unknown> {\n if (typeof raw !== 'string' || raw.length === 0) return {};\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/manifest/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAwB/D;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IAErC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAG9B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAG/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;CACrC;AAuCD;;;;;GAKG;AACH,qBAAa,eAAe;IAC1B;;OAEG;IACG,QAAQ,CACZ,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC;IAkC/B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,iBAAiB;IAmBzB;;OAEG;YACW,aAAa;IAc3B;;OAEG;YACW,gBAAgB;IAoD9B;;;;;OAKG;YACW,oBAAoB;
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/manifest/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAwB/D;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IAErC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAG9B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAG/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;CACrC;AAuCD;;;;;GAKG;AACH,qBAAa,eAAe;IAC1B;;OAEG;IACG,QAAQ,CACZ,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC;IAkC/B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,iBAAiB;IAmBzB;;OAEG;YACW,aAAa;IAc3B;;OAEG;YACW,gBAAgB;IAoD9B;;;;;OAKG;YACW,oBAAoB;IAuElC;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAiCtB;;OAEG;YACW,WAAW;IAyCzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAuCxB;;OAEG;YACW,yBAAyB;IA6EvC;;OAEG;YACW,uBAAuB;IAwCrC;;OAEG;IACH,OAAO,CAAC,eAAe;IAavB;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAuB5B"}
|
|
@@ -149,13 +149,10 @@ var ManifestBuilder = class {
|
|
|
149
149
|
typeAliases: results.typeAliases
|
|
150
150
|
});
|
|
151
151
|
if (config.smrtDependencies && config.smrtDependencies.length > 0) manifest.smrtDependencies = config.smrtDependencies;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
manifestGen.generateSchemas(manifest);
|
|
157
|
-
manifestGen.assertTenantScopedSchemaContract(manifest);
|
|
158
|
-
manifestGen.generateAgentManifests(manifest, packageName, packageJson);
|
|
152
|
+
new ManifestGenerator().applyGenerationPasses(manifest, {
|
|
153
|
+
packageName,
|
|
154
|
+
packageJson
|
|
155
|
+
});
|
|
159
156
|
return manifest;
|
|
160
157
|
}
|
|
161
158
|
/**
|