@happyvertical/smrt-agents 0.43.2 → 0.43.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["items","tenantId","tenantId"],"sources":["../src/__smrt-register__.ts","../src/ai-config.ts","../src/interests.ts","../src/learning.ts","../src/agent.ts","../src/data-surface.ts","../src/delegation.ts","../src/invoke-agent.ts","../src/schedule.ts","../src/tenant-agent.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// During library builds, smrtPlugin replaces this entire module with generated\n// code that embeds the scanned manifest inline (#1506/#1507) — published dists\n// never resolve this URL, so downstream bundlers cannot break registration by\n// relocating the compiled module away from dist/manifest.json. The runtime\n// lookup below is the fallback for source-mode runs without that transform.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","import type { AIClientOptions } from '@happyvertical/ai';\nimport { SecretService } from '@happyvertical/smrt-secrets';\nimport { getCurrentTenant, withTenant } from '@happyvertical/smrt-tenancy';\nimport { TenantCollection } from '@happyvertical/smrt-users';\nimport type { DatabaseInterface } from '@happyvertical/sql';\n\nexport type AgentAISecretFallback = 'none' | 'ancestors';\n\nexport interface AgentAIOptions extends AIClientOptions {\n /**\n * Secret name to resolve for the provider API key.\n *\n * When omitted, the agent runtime falls back to a provider-specific default\n * for known providers such as Gemini, OpenAI, and Anthropic.\n */\n apiKeySecretName?: string;\n\n /**\n * Whether to fall back to ancestor tenants when the current tenant does not\n * define the requested secret.\n *\n * Defaults to `'ancestors'`.\n */\n apiKeySecretFallback?: AgentAISecretFallback;\n}\n\ninterface ResolveAgentAIOptionsInput {\n aiConfig: AgentAIOptions | undefined;\n db: DatabaseInterface | null | undefined;\n tenantId?: string | null;\n}\n\nconst DEFAULT_SECRET_NAMES: Record<string, string> = {\n anthropic: 'ANTHROPIC_API_KEY',\n gemini: 'GEMINI_API_KEY',\n openai: 'OPENAI_API_KEY',\n};\n\nconst DEFAULT_SECRET_FALLBACK: AgentAISecretFallback = 'ancestors';\n\nconst secretServiceCache = new WeakMap<\n DatabaseInterface,\n Promise<SecretService>\n>();\nconst tenantCollectionCache = new WeakMap<\n DatabaseInterface,\n Promise<TenantCollection>\n>();\n\nfunction asNonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim().length > 0\n ? value.trim()\n : undefined;\n}\n\nfunction normalizeSecretFallback(value: unknown): AgentAISecretFallback {\n return value === 'none' ? 'none' : DEFAULT_SECRET_FALLBACK;\n}\n\nfunction getDefaultSecretName(aiConfig: AgentAIOptions): string | undefined {\n const provider = asNonEmptyString(aiConfig.type)?.toLowerCase();\n if (!provider) {\n return undefined;\n }\n\n return DEFAULT_SECRET_NAMES[provider];\n}\n\nfunction stripAgentAISecretFields(\n aiConfig: AgentAIOptions,\n): AIClientOptions & Record<string, unknown> {\n const {\n apiKeySecretName: _apiKeySecretName,\n apiKeySecretFallback: _apiKeySecretFallback,\n ...rest\n } = aiConfig;\n return rest;\n}\n\nasync function getSecretService(db: DatabaseInterface): Promise<SecretService> {\n const existing = secretServiceCache.get(db);\n if (existing) {\n return await existing;\n }\n\n const created = SecretService.create({ db });\n secretServiceCache.set(db, created);\n return await created;\n}\n\nasync function getTenantCollection(\n db: DatabaseInterface,\n): Promise<TenantCollection> {\n const existing = tenantCollectionCache.get(db);\n if (existing) {\n return await existing;\n }\n\n const created = TenantCollection.create({ db });\n tenantCollectionCache.set(db, created);\n return await created;\n}\n\nasync function getTenantSearchOrder(\n db: DatabaseInterface,\n tenantId: string,\n fallback: AgentAISecretFallback,\n): Promise<string[]> {\n const tenantIds = [tenantId];\n if (fallback !== 'ancestors') {\n return tenantIds;\n }\n\n const tenants = await getTenantCollection(db);\n const ancestors = await tenants.getAncestors(tenantId);\n for (const tenant of ancestors) {\n if (tenant.id) {\n tenantIds.push(tenant.id);\n }\n }\n\n return tenantIds;\n}\n\nasync function resolveSecretValue(\n service: SecretService,\n tenantIds: string[],\n secretName: string,\n): Promise<string | undefined> {\n for (const tenantId of tenantIds) {\n const value = await withTenant({ tenantId }, async () => {\n try {\n return (await service.retrieve(secretName)).value;\n } catch (error) {\n if (isMissingSecretError(error, secretName)) {\n return undefined;\n }\n\n throw error;\n }\n });\n\n if (value) {\n return value;\n }\n }\n\n return undefined;\n}\n\nfunction isMissingSecretError(error: unknown, secretName: string): boolean {\n if (!(error instanceof Error)) {\n return false;\n }\n\n return (\n error.message === `Secret '${secretName}' not found` ||\n error.message === 'Secret not found'\n );\n}\n\nexport async function resolveAgentAIOptions(\n input: ResolveAgentAIOptionsInput,\n): Promise<AIClientOptions | undefined> {\n const { aiConfig, db } = input;\n if (!aiConfig) {\n return undefined;\n }\n\n const normalized = { ...aiConfig };\n if (asNonEmptyString(normalized.apiKey)) {\n return stripAgentAISecretFields(normalized);\n }\n\n const secretName =\n asNonEmptyString(normalized.apiKeySecretName) ??\n getDefaultSecretName(normalized);\n if (!secretName || !db) {\n return stripAgentAISecretFields(normalized);\n }\n\n const tenantId =\n asNonEmptyString(input.tenantId) ??\n asNonEmptyString(getCurrentTenant()?.tenantId);\n if (!tenantId) {\n return stripAgentAISecretFields(normalized);\n }\n\n const fallback = normalizeSecretFallback(normalized.apiKeySecretFallback);\n const tenantIds = await getTenantSearchOrder(db, tenantId, fallback);\n const service = await getSecretService(db);\n const apiKey = await resolveSecretValue(service, tenantIds, secretName);\n\n if (!apiKey) {\n return stripAgentAISecretFields(normalized);\n }\n\n return {\n ...stripAgentAISecretFields(normalized),\n apiKey,\n };\n}\n","import type { SmrtClassOptions, SmrtObject } from '@happyvertical/smrt-core';\n\n// Forward reference for Agent type (avoids circular dependency).\n// The actual Agent class is in agent.ts which imports from this file. We model\n// only the structural surface a handler relies on (the agent's `options`); a\n// concrete class instance is not assignable to a type with a string index\n// signature, so handlers needing richer access should specialize the `A`\n// type parameter with their concrete agent type.\ntype AgentLike = {\n options: SmrtClassOptions;\n};\n\n/**\n * Handler function that processes a single matched interest item\n *\n * Called for each item after filtering/qualification. Use to determine\n * what action to take for each matched item.\n *\n * @param item - The matched SmrtObject\n * @param agent - The agent instance (for accessing agent context/methods)\n * @returns An action descriptor object (or any value)\n *\n * @example\n * ```typescript\n * // Simple action descriptor\n * handler: async (meeting) => ({\n * action: 'recap',\n * meeting\n * })\n *\n * // Using agent context\n * handler: async (meeting, agent) => ({\n * action: 'analyze',\n * config: agent.config,\n * priority: meeting.isUrgent ? 'high' : 'normal'\n * })\n * ```\n */\nexport type InterestHandlerFn<\n T extends SmrtObject = SmrtObject,\n A extends AgentLike = AgentLike,\n R = unknown,\n> = (item: T, agent: A) => Promise<R> | R;\n\n/**\n * Filter object using SDK SQL operator-in-key pattern (AND-only for now)\n *\n * Supports operators in keys:\n * - `{ 'status': 'active' }` → WHERE status = 'active'\n * - `{ 'price >': 100 }` → WHERE price > 100\n * - `{ 'type in': ['a', 'b'] }` → WHERE type IN ('a', 'b')\n *\n * Supported operators: =, >, <, >=, <=, !=, in, like\n */\nexport type ObjectFilter = Record<string, unknown>;\n\n/**\n * Async qualifier function for post-filter processing\n *\n * Receives items after SQL filtering, returns filtered/modified items.\n * Use for filtering that can't be expressed in SQL (e.g., AI-based filtering).\n *\n * @example\n * ```typescript\n * const qualify: AsyncQualifierFn<Meeting> = async (meetings) => {\n * return meetings.filter(m => m.isPublic);\n * };\n * ```\n */\nexport type AsyncQualifierFn<T extends SmrtObject = SmrtObject> = (\n items: T[],\n) => Promise<T[]>;\n\n/**\n * Custom query function for complex SQL patterns\n *\n * Returns a WHERE clause and parameters for use with collection.query().\n * Use for patterns that can't be expressed with standard filters:\n * - NOT EXISTS subqueries\n * - JOINs with other tables\n * - Complex OR conditions\n * - Window functions\n *\n * @param tableName - The main table name (aliased as 't' in the query)\n * @returns Tuple of [whereClause, params] to append to query\n *\n * @example\n * ```typescript\n * // Find meetings without corresponding recaps\n * const query: QueryFn = (t) => [\n * `${t}.start_date < datetime('now') AND NOT EXISTS (\n * SELECT 1 FROM contents c\n * WHERE c.meeting_id = ${t}.id\n * AND c._meta_type = 'MeetingRecap'\n * )`,\n * []\n * ];\n * ```\n */\nexport type QueryFn = (tableName: string) => [sql: string, params: unknown[]];\n\n/**\n * Single interest filter configuration\n *\n * Supports either standard SDK filters OR custom query function, plus\n * optional sort, limit, and post-query qualification.\n */\nexport interface InterestFilter<T extends SmrtObject = SmrtObject> {\n /**\n * Optional label for this interest (useful for debugging/logging)\n */\n name?: string;\n\n /**\n * SQL filter object for queries (standard SDK filter)\n * Merged with global filter using AND logic (object spread)\n *\n * Use this for simple AND conditions with standard operators.\n * For complex queries (NOT EXISTS, JOINs), use `query` instead.\n */\n filter?: ObjectFilter;\n\n /**\n * Custom query function for complex SQL patterns\n *\n * When provided, bypasses standard filter and uses collection.query()\n * with the generated SQL. Supports NOT EXISTS, JOINs, CTEs, etc.\n *\n * Cannot be used together with `filter`.\n */\n query?: QueryFn;\n\n /**\n * SQL orderBy format: 'priority DESC' or ['priority DESC', 'name ASC']\n */\n sort?: string | string[];\n\n /**\n * Maximum number of items to return for this interest\n */\n limit?: number;\n\n /**\n * Async post-filter function on results\n * Runs after SQL query returns, enables AI-based or complex filtering\n */\n qualify?: AsyncQualifierFn<T>;\n\n /**\n * Handler function called for each matched item\n *\n * Use to determine what action to take for each item. The handler\n * receives the item and agent instance, and returns an action descriptor.\n *\n * @example\n * ```typescript\n * handler: async (meeting, agent) => ({\n * action: 'recap',\n * meeting,\n * config: agent.config\n * })\n * ```\n */\n handler?: InterestHandlerFn<T>;\n}\n\n/**\n * Configuration for a specific object type's interest\n *\n * Can be a single InterestFilter or an array of InterestFilters.\n * Arrays allow multiple independent queries for the same object type.\n *\n * @example\n * ```typescript\n * // Single filter (backward compatible)\n * const config: ObjectInterestConfig = {\n * filter: { status: 'active' },\n * sort: 'created_at DESC'\n * };\n *\n * // Multiple filters (new feature)\n * const config: ObjectInterestConfig = [\n * {\n * name: 'needs-analysis',\n * filter: { 'agendaUrl !=': null, status: 'scheduled' }\n * },\n * {\n * name: 'needs-recap',\n * query: (t) => [\n * `${t}.start_date < datetime('now') AND NOT EXISTS (\n * SELECT 1 FROM contents WHERE meeting_id = ${t}.id\n * )`,\n * []\n * ]\n * }\n * ];\n * ```\n */\nexport type ObjectInterestConfig<T extends SmrtObject = SmrtObject> =\n | InterestFilter<T>\n | InterestFilter<T>[];\n\n/**\n * Global interest configuration for an agent\n *\n * @example\n * ```typescript\n * const interests: InterestOptions = {\n * filter: { status: 'active' },\n * sort: 'created_at DESC',\n * objects: {\n * Meeting: {\n * sort: 'scheduled_at DESC',\n * filter: { 'scheduled_at >': new Date() },\n * limit: 10\n * },\n * Document: {\n * filter: { 'type in': ['agenda', 'minutes'] }\n * }\n * }\n * };\n * ```\n */\nexport interface InterestOptions {\n /**\n * Global sort applied to final combined results\n * If not specified, results are grouped by type with type-specific sorts\n */\n sort?: string | string[];\n\n /**\n * Global filter applied to all object types\n * Merged with object-specific filters using AND logic\n */\n filter?: ObjectFilter;\n\n /**\n * Global async qualifier applied after all object-specific qualifiers\n */\n qualify?: AsyncQualifierFn;\n\n /**\n * Object-specific interest configurations\n * Keys must match ObjectRegistry class names (case-insensitive lookup)\n */\n objects: {\n [className: string]: ObjectInterestConfig;\n };\n}\n\n/**\n * Result item from interesting() method\n *\n * @example\n * ```typescript\n * const items = await agent.interesting();\n * for (const { type, data, name, handled } of items) {\n * console.log(`${type} from filter \"${name}\": action=${handled?.action}`);\n * }\n * ```\n */\nexport interface InterestResult<\n T extends SmrtObject = SmrtObject,\n R = unknown,\n> {\n /**\n * Object class name from ObjectRegistry\n */\n type: string;\n\n /**\n * The actual SmrtObject instance\n */\n data: T;\n\n /**\n * Name of the filter that matched this item (if specified)\n * Useful for debugging and logging\n */\n name?: string;\n\n /**\n * Result from handler function (if handler was defined)\n * Contains the action descriptor returned by the handler\n */\n handled?: R;\n}\n\n/**\n * Extended agent options including interests\n */\nexport interface AgentWithInterestsOptions {\n /**\n * Interest configuration for this agent\n */\n interests?: InterestOptions;\n}\n\n/**\n * Merge global and object-specific filters via object spread.\n *\n * Non-colliding keys from both filters are combined (effectively AND-ing them\n * in the resulting query). On a key collision the object-specific value\n * **replaces** the global one — `{ ...global, ...object }` — so a per-object\n * filter overrides the global filter for that key. A global safety filter is\n * therefore NOT preserved when an object filter sets the same key; choose\n * distinct keys (or different operators) if both must apply.\n *\n * @param globalFilter - Global filter applied to all types\n * @param objectFilter - Object-specific filter (wins on key collision)\n * @returns Merged filter object\n *\n * @example\n * ```typescript\n * // Distinct keys are combined:\n * mergeFilters({ status: 'active' }, { 'created_at >': date })\n * // Returns: { status: 'active', 'created_at >': date }\n *\n * // Colliding key: the object value replaces the global one:\n * mergeFilters({ status: 'active' }, { status: 'archived' })\n * // Returns: { status: 'archived' }\n * ```\n */\nexport function mergeFilters(\n globalFilter?: ObjectFilter,\n objectFilter?: ObjectFilter,\n): ObjectFilter {\n if (!globalFilter && !objectFilter) return {};\n if (!globalFilter) return { ...objectFilter };\n if (!objectFilter) return { ...globalFilter };\n return { ...globalFilter, ...objectFilter };\n}\n\n/**\n * Normalize sort to array format\n *\n * @param sort - Sort specification (string or array)\n * @returns Array of sort fields\n *\n * @example\n * ```typescript\n * normalizeSort('created_at DESC')\n * // Returns: ['created_at DESC']\n *\n * normalizeSort(['priority DESC', 'name ASC'])\n * // Returns: ['priority DESC', 'name ASC']\n * ```\n */\nexport function normalizeSort(sort?: string | string[]): string[] {\n if (!sort) return [];\n return Array.isArray(sort) ? sort : [sort];\n}\n","/**\n * Opt-in Learning trait configuration for {@link Agent} (#1886).\n *\n * The trait is **off by default**: an agent that does not declare\n * `static learning` behaves byte-for-byte as it does today. Declaring it — with\n * a single `static learning = true` (or a config object) — wires a\n * confidence-scored recall-before / capture-after loop into the agent\n * lifecycle, backed by {@link LearningMemory} from `@happyvertical/smrt-core`.\n *\n * @module\n */\n\nimport type { LearningMemoryConfig } from '@happyvertical/smrt-core';\n\n/**\n * Per-agent learning configuration. All fields are optional; omitted\n * thresholds fall back to {@link LearningMemory}'s defaults (the proven\n * `praeco` values: reuse floor 0.7, success 0.9, failure 0.3).\n */\nexport interface AgentLearningConfig {\n /** Explicit enable flag. Defaults to `true` when a config object is given. */\n enabled?: boolean;\n /**\n * Base memory scope for this agent. Defaults to `agent/<agentType>`.\n * Recall/capture are additionally isolated by the agent instance id (owner),\n * so two tenants running the same agent class never share memory.\n */\n scope?: string;\n /** Reuse floor — recall omits memories below this confidence. Default 0.7. */\n minConfidence?: number;\n /** Confidence a 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 /** Reinforcement blend weight in `[0, 1]`. Default 0.5. */\n reinforcement?: number;\n /** Optional half-life (ms) for time-based confidence decay. */\n decayHalfLifeMs?: number;\n}\n\n/**\n * The `static learning` declaration accepted on an {@link Agent} subclass:\n * `false` (default, off), `true` (on with defaults), or a config object.\n */\nexport type AgentLearningDeclaration = AgentLearningConfig | boolean;\n\n/** Normalised learning settings resolved from a declaration. */\nexport interface ResolvedAgentLearning {\n enabled: boolean;\n scope?: string;\n /** Threshold overrides to pass to `LearningMemory` (only defined keys). */\n memoryConfig: Partial<LearningMemoryConfig>;\n}\n\n/**\n * Resolve a `static learning` declaration into normalised settings.\n *\n * Only keys explicitly set on the declaration are forwarded to\n * `LearningMemory`, so unset thresholds keep the module's defaults rather than\n * clobbering them with `undefined`.\n */\nexport function resolveAgentLearning(\n declaration: AgentLearningDeclaration | undefined,\n): ResolvedAgentLearning {\n if (declaration === undefined || declaration === false) {\n return { enabled: false, memoryConfig: {} };\n }\n if (declaration === true) {\n return { enabled: true, memoryConfig: {} };\n }\n\n const memoryConfig: Partial<LearningMemoryConfig> = {};\n if (declaration.minConfidence !== undefined) {\n memoryConfig.minConfidence = declaration.minConfidence;\n }\n if (declaration.successConfidence !== undefined) {\n memoryConfig.successConfidence = declaration.successConfidence;\n }\n if (declaration.failureConfidence !== undefined) {\n memoryConfig.failureConfidence = declaration.failureConfidence;\n }\n if (declaration.reinforcement !== undefined) {\n memoryConfig.reinforcement = declaration.reinforcement;\n }\n if (declaration.decayHalfLifeMs !== undefined) {\n memoryConfig.decayHalfLifeMs = declaration.decayHalfLifeMs;\n }\n\n return {\n enabled: declaration.enabled ?? true,\n scope: declaration.scope,\n memoryConfig,\n };\n}\n","import type { AIClientOptions } from '@happyvertical/ai';\nimport { createLogger, type Logger } from '@happyvertical/logger';\nimport { sanitizeConfig } from '@happyvertical/smrt-config';\nimport {\n type ConfigResolver,\n createDispatchBus,\n type DispatchBus,\n type DispatchMetadata,\n type DispatchTenantScope,\n type LearningEpisode,\n LearningMemory,\n type LearningMemoryRecord,\n type LearningOutcome,\n type LearningSemanticSearch,\n ObjectRegistry,\n resolveDispatchTenantScope,\n type SmrtCollection,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport {\n getCurrentTenant,\n TenantScoped,\n tenantId,\n} from '@happyvertical/smrt-tenancy';\nimport { type AgentAIOptions, resolveAgentAIOptions } from './ai-config.js';\nimport { AgentConfig } from './config.js';\nimport {\n instanceScopedSubscriber,\n getAgentClassName as resolveAgentClassName,\n getAgentTypeName as resolveAgentTypeName,\n} from './identity.js';\nimport type {\n AgentWithInterestsOptions,\n InterestFilter,\n InterestOptions,\n InterestResult,\n ObjectFilter,\n ObjectInterestConfig,\n} from './interests.js';\nimport { mergeFilters, normalizeSort } from './interests.js';\nimport {\n type AgentLearningDeclaration,\n resolveAgentLearning,\n} from './learning.js';\nimport type { AgentStatusType } from './types.js';\nimport type { AgentAdminRoute, AgentUISlots } from './ui.js';\n\n/**\n * Agent constructor options\n */\nexport interface AgentOptions\n extends SmrtObjectOptions,\n AgentWithInterestsOptions {\n /**\n * Optional AI configuration for this agent.\n *\n * When `apiKey` is omitted, the runtime can resolve provider credentials from\n * tenant secrets based on the active tenant context.\n */\n ai?: AgentAIOptions;\n /**\n * Suppress all log output (useful for CLI --json mode)\n * When true, creates a no-op logger that discards all messages\n */\n silent?: boolean;\n /**\n * Opt into process-level SIGTERM/SIGINT handling for this instance.\n *\n * Host runtimes should generally own process lifecycle; this remains available\n * for single-agent CLIs and scripts that explicitly want it. Do not enable\n * this for multiple agents in the same process unless the host coordinates\n * shutdown itself; the first handler to finish exits the process.\n */\n manageProcessSignals?: boolean;\n\n /**\n * Durable per-instance key for multi-instance agents (#1890).\n *\n * Only honored when the agent class opts into multi-instance\n * (`static multiInstance = true`); a singleton agent (the default) ignores it,\n * so passing a key can never change a non-opted agent's behavior. When honored\n * it becomes the per-instance dispatch subscriber suffix and memory partition\n * (see {@link Agent.getDispatchSubscriber} / {@link Agent.learningScope}) so N\n * instances of one class run independently. Typically the persona id from\n * `@happyvertical/smrt-personas` (a persona is a durable instance).\n */\n instanceKey?: string | null;\n\n /**\n * Durable persona row that owns this agent's editable settings.\n *\n * This is deliberately independent from `instanceKey`: the reserved default\n * persona keeps the singleton runtime identity (`instanceKey: null`) but must\n * still load and save its own persona-scoped settings.\n */\n personaId?: string | null;\n}\n\n/**\n * Base Agent class for building autonomous actors in the SMRT ecosystem\n *\n * Agents are SmrtObjects that perform specific tasks with:\n * - Status tracking (idle, initializing, running, error, shutdown)\n * - Configuration management via @have/config\n * - Structured logging via @happyvertical/logger\n * - Lifecycle hooks (initialize, validate, run, shutdown)\n * - Optional process signal handling for graceful shutdown\n *\n * Agents can define their own properties for state management - since they extend\n * SmrtObject, any properties defined will be automatically persisted to the database.\n *\n * **Important**: Extending classes must add the `@smrt()` decorator themselves\n * to configure CLI/API/MCP exposure.\n *\n * @example\n * ```typescript\n * import { Agent } from '@have/agents';\n * import { getModuleConfig } from '@have/config';\n * import { smrt } from '@happyvertical/smrt-core';\n *\n * @smrt()\n * class MyAgent extends Agent {\n * protected config = getModuleConfig('my-agent', {\n * cronSchedule: '0 2 * * *',\n * maxRetries: 3\n * });\n *\n * // Define your own state properties (automatically persisted)\n * lastCrawl: Date | null = null;\n * itemsProcessed: number = 0;\n *\n * async validate(): Promise<void> {\n * if (!this.config.cronSchedule) {\n * throw new Error('cronSchedule is required');\n * }\n * }\n *\n * async run(): Promise<void> {\n * // Agent logic here\n * this.itemsProcessed = 42;\n * this.lastCrawl = new Date();\n * await this.save(); // Persist state\n * }\n * }\n *\n * const agent = new MyAgent({ name: 'my-agent' });\n * await agent.execute();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // Abstract class - no direct CLI/API/MCP exposure\n // But must be registered for inheritance chain to work (issue #523)\n cli: false,\n api: false,\n mcp: false,\n // STI: All agents share 'agents' table for polymorphic queries\n tableStrategy: 'sti',\n})\nexport abstract class Agent extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global agents\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * UI slots this agent supports for admin panels\n *\n * Subclasses override this to declare their admin UI slots.\n * Each slot can be implemented by a Svelte component.\n *\n * @example\n * ```typescript\n * static override uiSlots: AgentUISlots = {\n * sources: {\n * id: 'sources',\n * label: 'News Sources',\n * description: 'Configure scrapers and data sources',\n * icon: 'database',\n * order: 1,\n * },\n * settings: {\n * id: 'settings',\n * label: 'Agent Settings',\n * description: 'Configure agent behavior',\n * icon: 'settings',\n * order: 2,\n * },\n * };\n * ```\n */\n static uiSlots: AgentUISlots = {};\n\n /**\n * Admin routes this agent provides\n *\n * Subclasses override this to declare admin route metadata.\n * The vitePluginAgentRoutes Vite plugin reads these from the manifest\n * and registers them so host applications can discover and render them.\n *\n * @example\n * ```typescript\n * static override adminRoutes: AgentAdminRoute[] = [\n * { path: 'sources', component: 'SourcesPanel', load: 'loadSources' },\n * { path: 'sources/[sourceId]', component: 'SourceDetail', load: 'loadSourceDetail' },\n * ];\n * ```\n */\n static adminRoutes: AgentAdminRoute[] = [];\n\n /**\n * Signal types this agent subscribes to by default\n *\n * These are seedable defaults — on `initialize()`, the agent checks the\n * database first and only creates subscriptions that don't already exist.\n * The database is the runtime source of truth, allowing users to customize\n * subscriptions per-tenant via the dashboard without code changes.\n *\n * When declared, `execute()` will automatically call `processDispatches()`\n * before `run()`, so handler agents don't need to manually poll.\n * Override `handleDispatch()` to process incoming dispatches.\n *\n * @example\n * ```typescript\n * @smrt({ agent: { icon: 'mail', tier: 'standard' } })\n * class EmailHandler extends Agent {\n * static override signalSubscriptions = ['email.received', 'email.bounced'];\n *\n * async handleDispatch(payload: unknown, metadata: DispatchMetadata) {\n * // Called automatically during execute() for each pending dispatch\n * }\n *\n * async run() { ... }\n * }\n * ```\n */\n static signalSubscriptions: string[] = [];\n\n /**\n * Execute-time resolvers for `agent_config` fields that should be computed\n * lazily rather than snapshotted at sync time.\n *\n * Each entry is keyed by the agent_config field it produces. The runtime\n * (see {@link resolveLazyConfig}) calls every resolver and overlays the\n * results on top of the persisted config before constructing the agent.\n * That means env-derived values like asset storage paths, S3 buckets, AI\n * provider keys, or tenant-scoped DB URLs stay live: rotating an env var\n * is reflected on the next scheduled run without rewriting the schedule\n * row.\n *\n * Resolvers may be sync or async. Returning `undefined` or `null` leaves\n * the persisted value in place — both are treated as \"no overlay\" so the\n * common `() => process.env.X ?? null` pattern is safe and won't clobber\n * a snapshotted value when the env var is unset. Throwing falls back to\n * the persisted value (or to whatever\n * {@link ResolveLazyConfigOptions.onError} dictates).\n *\n * @example\n * ```typescript\n * class Praeco extends Agent {\n * static override configResolvers = {\n * assetStorage: () => resolveSharedAssetStorage(),\n * aiKey: async () => loadAIKeyFromSecretsManager(),\n * };\n * }\n * ```\n */\n static configResolvers: Record<string, ConfigResolver> = {};\n\n /**\n * Opt-in learning trait declaration (#1886).\n *\n * **Off by default.** Set to `true` (or a config object) on a subclass to\n * wire a confidence-scored recall-before / capture-after loop into the agent\n * lifecycle, backed by {@link LearningMemory}. A non-opted agent behaves\n * byte-for-byte as it does today — the learning branches are never entered.\n *\n * When enabled, the loop wraps `run()` itself (in {@link initialize}), so it\n * fires whether the agent runs via {@link execute} or the background/scheduled\n * path (which calls `run()` directly). Each run:\n * 1. recalls confident memories for {@link learningScope} before `run()`,\n * exposing them via {@link recalledMemories};\n * 2. captures the run outcome after `run()` — a clean completion reinforces\n * the staged memory (see {@link stageLearning}); a thrown error or an\n * explicit {@link reportLearningOutcome} failure decays it.\n *\n * @example\n * ```typescript\n * @smrt()\n * class InvoiceAgent extends Agent {\n * static override learning = true; // reuse floor 0.7, success 0.9, fail 0.3\n * // or: static override learning = { minConfidence: 0.8, scope: 'invoices' };\n * protected config = {};\n * async run() {\n * const [cached] = this.recalledMemories;\n * const strategy = cached?.value ?? (await this.generateStrategy());\n * this.stageLearning({ scope: this.learningScope(), key: 'default', value: strategy });\n * }\n * }\n * ```\n */\n static learning: AgentLearningDeclaration = false;\n\n /**\n * Opt into multiple durable instances of this agent class per tenant (#1890).\n *\n * **Off by default** — a non-opted class is a **singleton** (the N=1 case) and\n * behaves byte-for-byte as it does today: one dispatch subscriber keyed by the\n * agent type, one memory scope, class-wide interests. Setting this to `true`\n * lets N configured instances (personas, from `@happyvertical/smrt-personas`)\n * run independently: each is constructed with its own {@link AgentOptions.instanceKey},\n * which the framework folds into a per-instance dispatch subscriber\n * ({@link getDispatchSubscriber}), memory partition ({@link learningScope}),\n * and interest/subscription scoping seams ({@link instanceInterestFilter} /\n * {@link resolveSignalSubscriptions}) so two instances never double-process\n * each other's dispatches or interests.\n *\n * The framework provides the per-instance *identity*; a package scopes its own\n * dispatch/interests to the instance's config by overriding the seams. The\n * `default` persona reuses the singleton identity (null key), which makes the\n * singleton→multi upgrade non-destructive.\n */\n static multiInstance: boolean = false;\n\n /**\n * Current agent status\n */\n status: AgentStatusType = 'idle';\n\n /**\n * Structured logger instance\n * Created with agent's class name as context\n */\n protected logger: Logger;\n\n /**\n * Agent configuration\n * Must be defined by extending classes using getModuleConfig()\n *\n * @example\n * ```typescript\n * protected config = getModuleConfig('my-agent', {\n * cronSchedule: '0 0 * * *',\n * maxRetries: 3\n * });\n * ```\n */\n protected abstract config: unknown;\n\n /**\n * Signal handlers for graceful shutdown\n */\n private signalHandlers: Map<NodeJS.Signals, () => void> = new Map();\n\n /**\n * Cached DispatchBus instance for inter-agent communication\n */\n private _dispatch: DispatchBus | null = null;\n\n /**\n * Cached LearningMemory binding, once successfully built. Not cached when\n * learning is disabled or the DB isn't ready yet, so an early call can't\n * permanently stick the agent in a learning-disabled state.\n */\n private _learningMemory?: LearningMemory;\n\n /**\n * Whether `run()` has been wrapped with the learning loop (idempotency guard).\n */\n private _runWrappedForLearning = false;\n\n /**\n * The episode the current run acted on, staged via {@link stageLearning} so\n * the lifecycle can reinforce it after `run()`.\n */\n private _learningEpisode: LearningEpisode | null = null;\n\n /**\n * Explicit outcome for the current run, set via\n * {@link reportLearningOutcome}. When unset, a clean `run()` is treated as a\n * success and a thrown error as a failure.\n */\n private _learningOutcome: LearningOutcome | null = null;\n\n /**\n * Memories recalled before `run()` when the learning trait is enabled.\n *\n * Empty for non-opted agents. Populated by the lifecycle (see\n * {@link recallForRun}); read from `run()` to reuse prior knowledge.\n */\n protected recalledMemories: LearningMemoryRecord[] = [];\n\n /**\n * Creates a new Agent instance\n *\n * @param options - Configuration options including identifiers and metadata\n */\n constructor(options: AgentOptions = {}) {\n super(options);\n // Use no-op logger in silent mode (for CLI --json output)\n this.logger = createLogger(options.silent ? false : { level: 'info' });\n }\n\n /**\n * Interest configuration for this agent\n * Lazily accessed from options on first interesting() call\n */\n protected get interests(): InterestOptions | undefined {\n return (this.options as AgentOptions).interests;\n }\n\n /**\n * Canonical agent type for persistence and dispatch routing.\n */\n protected getAgentTypeName(): string {\n const metaType = (this as { _meta_type?: unknown })._meta_type;\n if (typeof metaType === 'string' && metaType.length > 0) {\n return resolveAgentTypeName(metaType);\n }\n\n return resolveAgentTypeName(this.constructor.name);\n }\n\n /**\n * Human-readable class name for logs and UI.\n */\n protected getAgentClassName(): string {\n return resolveAgentClassName(this.getAgentTypeName());\n }\n\n // ============================================================================\n // Multi-instance identity (#1890) — opt-in; singleton (null key) by default\n // ============================================================================\n\n /**\n * Whether this agent class opted into multiple durable instances per tenant.\n */\n protected isMultiInstance(): boolean {\n return (this.constructor as typeof Agent).multiInstance === true;\n }\n\n /**\n * The durable per-instance key for this agent, or `null` for a singleton.\n *\n * Returns `null` unless the class opts in (`static multiInstance = true`) AND a\n * non-empty {@link AgentOptions.instanceKey} was supplied — so a non-opted\n * agent is always singleton-identified even if a key is passed. This is the\n * anchor the framework folds into the dispatch subscriber, memory scope, and\n * scoping seams below.\n */\n getInstanceKey(): string | null {\n if (!this.isMultiInstance()) {\n return null;\n }\n const key = (this.options as AgentOptions).instanceKey;\n return typeof key === 'string' && key.length > 0 ? key : null;\n }\n\n /**\n * Durable owner id used for database-backed slot configuration.\n *\n * Persona-backed agents use the persona row id, including the default\n * persona whose runtime instance key remains null. Legacy/singleton agents\n * continue to use their persisted Agent STI row id.\n */\n getConfigOwnerId(slotId?: string): string | null {\n const personaId = (this.options as AgentOptions).personaId;\n const scope = slotId ? this.getUISlots()[slotId]?.scope : undefined;\n if (scope === 'persona') {\n return typeof personaId === 'string' && personaId.length > 0\n ? personaId\n : null;\n }\n if (scope === 'agent') {\n return this.id ?? null;\n }\n if (typeof personaId === 'string' && personaId.length > 0) {\n return personaId;\n }\n return this.id ?? null;\n }\n\n /**\n * Canonical dispatch subscriber identity for this agent.\n *\n * A singleton (no instance key) is the bare agent type — **unchanged** from the\n * class-keyed behavior. A multi-instance agent is `` `${agentType}#${key}` ``,\n * giving each instance its own subscription rows and its own pending-dispatch\n * queue so instances don't compete for or double-process each other's\n * dispatches. Used everywhere the agent subscribes, seeds, and processes.\n */\n getDispatchSubscriber(): string {\n return instanceScopedSubscriber(\n this.getAgentTypeName(),\n this.getInstanceKey(),\n );\n }\n\n /**\n * The signal types this instance should seed as dispatch subscriptions.\n *\n * Defaults to the class's static {@link Agent.signalSubscriptions} unchanged.\n * A multi-instance package overrides this to derive **instance-scoped** signal\n * types from the persona/instance config (e.g. append the instance key or a\n * routing dimension), so an emit meant for one instance only matches that\n * instance's subscription and the other never processes it.\n */\n protected resolveSignalSubscriptions(): string[] {\n return (this.constructor as typeof Agent).signalSubscriptions;\n }\n\n /**\n * An optional filter AND-merged (as the base layer) into every\n * {@link interesting} query for this instance.\n *\n * `undefined` by default (no scoping — singleton behavior unchanged). A\n * multi-instance package overrides it to return an instance-discriminating\n * filter derived from the persona/instance config, so two instances of one\n * class partition the objects they process and never double-handle the same\n * row. Global and per-object interest filters layer on top (and win on key\n * collision), so choose a dedicated discriminator key here.\n *\n * Applies to the standard filter path; custom `query` interest filters own\n * their SQL and should incorporate {@link getInstanceKey} themselves.\n */\n protected instanceInterestFilter(): ObjectFilter | undefined {\n return undefined;\n }\n\n /**\n * Get UI slot definitions for this agent instance\n *\n * Returns the static uiSlots defined on the agent's class.\n * Used by host applications to discover available admin panels.\n *\n * @example\n * ```typescript\n * const slots = agent.getUISlots();\n * for (const [slotId, slot] of Object.entries(slots)) {\n * console.log(`${slot.label}: ${slot.description}`);\n * }\n * ```\n */\n getUISlots(): AgentUISlots {\n return (this.constructor as typeof Agent).uiSlots;\n }\n\n // ============================================================================\n // Configuration Management\n // ============================================================================\n\n /**\n * Load all database-persisted configs for this agent\n *\n * Returns a Map of slotId → configData for all saved configurations.\n * Use getMergedConfig() to get file + db merged config for a slot.\n *\n * @returns Map of slotId to config data\n *\n * @example\n * ```typescript\n * const configs = await agent.loadConfigs();\n * const sources = configs.get('sources');\n * ```\n */\n async loadConfigs(): Promise<Map<string, Record<string, unknown>>> {\n const ownerIds = Array.from(\n new Set(\n [\n this.getConfigOwnerId(),\n this.id ?? null,\n (this.options as AgentOptions).personaId ?? null,\n ].filter((id): id is string => typeof id === 'string' && id.length > 0),\n ),\n );\n if (ownerIds.length === 0) {\n throw new Error(\n 'Agent must have a personaId or be saved before loading configs',\n );\n }\n const byOwner = await AgentConfig.forAgents(ownerIds, this.options);\n const result = new Map<string, Record<string, unknown>>();\n for (const [ownerId, configs] of byOwner) {\n for (const [slotId, config] of configs) {\n if (this.getConfigOwnerId(slotId) === ownerId) {\n result.set(slotId, config);\n }\n }\n }\n return result;\n }\n\n /**\n * Save config for a specific UI slot to the database\n *\n * Persists configuration data that can be modified by admin panels.\n * Use this when the user saves changes in an admin UI.\n *\n * @param slotId - The UI slot ID (e.g., 'sources', 'settings')\n * @param data - Configuration data to save\n *\n * @example\n * ```typescript\n * await agent.saveSlotConfig('sources', {\n * scrapers: ['civicweb', 'govstack'],\n * refreshInterval: 3600\n * });\n * ```\n */\n async saveSlotConfig(\n slotId: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n const ownerId = this.getConfigOwnerId(slotId);\n if (!ownerId) {\n throw new Error(\n 'Agent must have a personaId or be saved before saving slot config',\n );\n }\n await AgentConfig.saveSlot(\n {\n agentId: ownerId,\n agentClass: this.getAgentTypeName(),\n slotId,\n configData: data,\n },\n this.options,\n );\n }\n\n /**\n * Get merged config for a slot (file-based + database)\n *\n * Priority order (highest to lowest):\n * 1. Database-persisted config (from saveSlotConfig)\n * 2. File-based config (from getModuleConfig)\n * 3. Agent class defaults\n *\n * @param slotId - The UI slot ID\n * @returns Merged configuration object\n *\n * @example\n * ```typescript\n * const sourcesConfig = await agent.getMergedConfig('sources');\n * // Returns file config merged with any db overrides\n * ```\n */\n async getMergedConfig(slotId: string): Promise<Record<string, unknown>> {\n // Get file-based config from module config\n const fileConfig =\n ((this.config as Record<string, unknown>)?.[slotId] as\n | Record<string, unknown>\n | undefined) ?? {};\n\n const ownerId = this.getConfigOwnerId(slotId);\n if (!ownerId) {\n return fileConfig;\n }\n\n // Get db-persisted config\n const dbConfig = await AgentConfig.forSlot(ownerId, slotId, this.options);\n\n // Merge: db overrides file\n return { ...fileConfig, ...(dbConfig ?? {}) };\n }\n\n /**\n * Export all config for this agent (for static site generation)\n *\n * Merges file-based and database configs, then optionally sanitizes\n * to remove secrets. Use this before building a static site.\n *\n * @param options - Export options\n * @param options.includeSecrets - If true, includes API keys and secrets (default: false)\n * @returns Merged configuration object\n *\n * @example\n * ```typescript\n * // Export for static build (secrets filtered)\n * const config = await agent.exportConfig();\n *\n * // Export with secrets (for secure environments)\n * const fullConfig = await agent.exportConfig({ includeSecrets: true });\n * ```\n */\n async exportConfig(options?: {\n includeSecrets?: boolean;\n }): Promise<Record<string, unknown>> {\n const dbConfigs = await this.loadConfigs();\n const fileConfig = (this.config as Record<string, unknown>) ?? {};\n\n // Merge all configs\n const merged: Record<string, unknown> = { ...fileConfig };\n for (const [slotId, data] of dbConfigs) {\n merged[slotId] = {\n ...(merged[slotId] as Record<string, unknown> | undefined),\n ...data,\n };\n }\n\n // Sanitize if secrets not included (uses centralized sanitizeConfig from smrt-config)\n if (!options?.includeSecrets) {\n return sanitizeConfig(merged) as Record<string, unknown>;\n }\n\n return merged;\n }\n\n /**\n * Get the DispatchBus for inter-agent communication\n *\n * Creates a DispatchBus lazily on first access. Requires database configuration.\n *\n * @example\n * ```typescript\n * // Emit a dispatch to other agents\n * await this.dispatch.emit('campaign.completed', {\n * campaignId: '123',\n * revenue: 5000\n * }, { source: this.constructor.name });\n *\n * // Subscribe to dispatches\n * await this.dispatch.subscribe({\n * signalType: 'campaign.*',\n * subscriber: this.constructor.name\n * });\n * ```\n *\n * @throws Error if database is not configured\n */\n async getDispatch(): Promise<DispatchBus> {\n if (!this._dispatch) {\n if (!this._db) {\n throw new Error(\n `Agent ${this.constructor.name} requires database configuration for dispatch. ` +\n `Ensure the agent is initialized with a db option.`,\n );\n }\n this._dispatch = await createDispatchBus({\n db: this._db,\n });\n }\n return this._dispatch;\n }\n\n /**\n * Handle incoming dispatches\n *\n * Override this method to process dispatches targeted at this agent.\n * Called when process() is invoked for this agent's subscriber name.\n *\n * @param payload - Dispatch payload data\n * @param metadata - Dispatch metadata including type, source, and timing\n *\n * @example\n * ```typescript\n * async handleDispatch(payload: unknown, metadata: DispatchMetadata): Promise<void> {\n * if (metadata.type === 'campaign.completed') {\n * const data = payload as { campaignId: string; revenue: number };\n * await this.recordRevenue(data.campaignId, data.revenue);\n * }\n * }\n * ```\n */\n async handleDispatch(\n _payload: unknown,\n _metadata: DispatchMetadata,\n ): Promise<void> {\n // Default implementation does nothing\n // Subclasses should override to process dispatches\n }\n\n /**\n * Process pending dispatches for this agent\n *\n * Finds and processes all pending dispatches that match this agent's subscriptions.\n * Uses handleDispatch() to process each dispatch.\n *\n * @returns Number of dispatches processed\n *\n * @example\n * ```typescript\n * // In your run() method\n * const processed = await this.processDispatches();\n * this.logger.info(`Processed ${processed} dispatches`);\n * ```\n */\n async processDispatches(): Promise<number> {\n const dispatch = await this.getDispatch();\n return dispatch.process(\n this.getDispatchSubscriber(),\n this.handleDispatch.bind(this),\n );\n }\n\n // ============================================================================\n // Learning Trait (#1886) — opt-in; inert unless `static learning` is set\n // ============================================================================\n\n /**\n * Base memory scope for this agent's learning.\n *\n * Defaults to the configured `scope` (if any) or `agent/<agentType>`.\n * Override to shape how memories are filed (e.g. per task type). Recall and\n * capture are additionally isolated by the agent instance id (owner), so\n * memory never bleeds across tenants running the same agent class.\n */\n protected learningScope(): string {\n const resolved = resolveAgentLearning(\n (this.constructor as typeof Agent).learning,\n );\n const base = resolved.scope ?? `agent/${this.getAgentTypeName()}`;\n // Partition memory per durable instance so two multi-instance personas learn\n // independently. Null key (singleton) leaves the scope unchanged.\n const instanceKey = this.getInstanceKey();\n return instanceKey ? `${base}#${instanceKey}` : base;\n }\n\n /**\n * Optional semantic-search arm for {@link LearningMemory}.\n *\n * Returns `undefined` by default (keyed-context recall only). Override to\n * wire embedding search — e.g. return a bound `collection.semanticSearch`.\n */\n protected getLearningSemanticSearch(): LearningSemanticSearch | undefined {\n return undefined;\n }\n\n /**\n * Resolve the tenant id used for the learning scope and semantic filtering.\n */\n private resolveLearningTenantId(): string | null {\n const contextTenant = getCurrentTenant()?.tenantId;\n if (typeof contextTenant === 'string') return contextTenant;\n return typeof this.tenantId === 'string' ? this.tenantId : null;\n }\n\n /**\n * Get this agent's {@link LearningMemory} binding, or `null` when learning is\n * disabled or no database is configured.\n *\n * Cheap and side-effect-free when the trait is off (returns `null` after a\n * single static-flag check), which keeps non-opted agents unchanged.\n */\n getLearningMemory(): LearningMemory | null {\n if (this._learningMemory) {\n return this._learningMemory;\n }\n\n const resolved = resolveAgentLearning(\n (this.constructor as typeof Agent).learning,\n );\n // Disabled is a stable answer (cheap static check, no need to cache). When\n // enabled but the DB isn't wired yet, return null WITHOUT caching so a later\n // call (after initialize()) can build the binding.\n if (!resolved.enabled || !this._db) {\n return null;\n }\n\n // Ensure a stable owner id so memory is bound to this instance.\n if (!this.id) {\n this.id = crypto.randomUUID();\n }\n\n this._learningMemory = new LearningMemory({\n db: this.systemDb,\n ownerClass: this.getAgentTypeName(),\n ownerId: this.id as string,\n tenantId: this.resolveLearningTenantId(),\n semanticSearch: this.getLearningSemanticSearch(),\n config: resolved.memoryConfig,\n });\n return this._learningMemory;\n }\n\n /**\n * Wrap `run()` with the recall-before / capture-after learning loop when the\n * trait is enabled, so it fires **however run() is invoked** — via\n * {@link execute} OR directly by the background/scheduled path\n * (`ScheduleRunner` → `TaskRunner` calls the agent's configured method, which\n * defaults to `run` and never goes through `execute()`). Both paths call\n * {@link initialize}, so wrapping here covers them. Idempotent, and a no-op\n * for non-opted agents (their `run()` is left untouched).\n */\n private wrapRunForLearning(): void {\n if (this._runWrappedForLearning) return;\n if (\n !resolveAgentLearning((this.constructor as typeof Agent).learning).enabled\n ) {\n return;\n }\n this._runWrappedForLearning = true;\n\n const originalRun = this.run.bind(this);\n (this as { run: () => Promise<void> }).run = async (): Promise<void> => {\n const memory = this.getLearningMemory();\n if (!memory) {\n await originalRun();\n return;\n }\n\n // Clear per-run learning state up front so a throw in recallForRun()\n // can't leave stale recalled memories from a previous run.\n this.recalledMemories = [];\n this._learningEpisode = null;\n this._learningOutcome = null;\n try {\n this.recalledMemories = await this.recallForRun(memory);\n await originalRun();\n await this.captureForRun(\n memory,\n this._learningOutcome ?? { success: true },\n );\n } catch (error) {\n // Capture the failure, but never mask the original error.\n try {\n await this.captureForRun(memory, {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n } catch (captureError) {\n this.logger.warn('Learning capture failed during error handling', {\n error: captureError,\n });\n }\n throw error;\n } finally {\n this._learningEpisode = null;\n this._learningOutcome = null;\n }\n };\n }\n\n /**\n * Recall relevant memories before `run()`.\n *\n * Default: a scope-wide, confidence-filtered recall of {@link learningScope}.\n * Override to shape the recall (e.g. a keyed lookup or a semantic query).\n */\n protected async recallForRun(\n memory: LearningMemory,\n ): Promise<LearningMemoryRecord[]> {\n return memory.recall(this.learningScope());\n }\n\n /**\n * Capture the run outcome after `run()`.\n *\n * Default: reinforce the memory staged via {@link stageLearning}. A no-op\n * when nothing was staged. Override for bespoke capture logic.\n */\n protected async captureForRun(\n memory: LearningMemory,\n outcome: LearningOutcome,\n ): Promise<void> {\n if (!this._learningEpisode) return;\n await memory.capture(this._learningEpisode, outcome);\n }\n\n /**\n * Stage the memory episode the current run acted on, so the lifecycle\n * reinforces it after `run()` completes. Call from `run()`.\n */\n protected stageLearning(episode: LearningEpisode): void {\n this._learningEpisode = episode;\n }\n\n /**\n * Report an explicit outcome for the current run (e.g. a validated failure\n * that did not throw). Overrides the default success/throw inference.\n */\n protected reportLearningOutcome(outcome: LearningOutcome): void {\n this._learningOutcome = outcome;\n }\n\n /**\n * Initialize the agent\n * Sets status to 'initializing' and sets up signal handlers\n *\n * Override to perform setup after construction, but always call super.initialize()\n *\n * @example\n * ```typescript\n * async initialize(): Promise<void> {\n * await super.initialize();\n * // Custom initialization logic\n * }\n * ```\n */\n async initialize(): Promise<this> {\n await super.initialize();\n this.status = 'initializing';\n this.logger.info('Agent initializing');\n\n const fileAiConfig =\n typeof this.config === 'object' &&\n this.config !== null &&\n 'ai' in (this.config as Record<string, unknown>) &&\n typeof (this.config as Record<string, unknown>).ai === 'object' &&\n (this.config as Record<string, unknown>).ai !== null\n ? ((this.config as Record<string, unknown>).ai as AgentAIOptions)\n : undefined;\n const configuredAi =\n ((this.options as AgentOptions).ai as AgentAIOptions | undefined) ??\n fileAiConfig;\n if (configuredAi && this._db) {\n const resolvedAi = await resolveAgentAIOptions({\n aiConfig: configuredAi,\n db: this._db,\n tenantId:\n getCurrentTenant()?.tenantId ||\n (typeof this.tenantId === 'string' ? this.tenantId : undefined),\n });\n if (resolvedAi) {\n (this.options as AgentOptions).ai = resolvedAi as AIClientOptions &\n Record<string, unknown>;\n }\n }\n\n if ((this.options as AgentOptions).manageProcessSignals) {\n this.setupSignalHandlers();\n }\n\n // Seed declarative signal subscriptions (DB is source of truth)\n if (this._db) {\n const dispatch = await this.getDispatch();\n await this.migrateLegacyDispatchSubscriptions(dispatch);\n\n const subs = this.resolveSignalSubscriptions();\n if (subs.length > 0) {\n const subscriber = this.getDispatchSubscriber();\n const existing = await dispatch.listSubscriptions(subscriber);\n const existingTypes = new Set(existing.map((s) => s.signalType));\n for (const signalType of subs) {\n if (!existingTypes.has(signalType)) {\n await dispatch.subscribe({\n signalType,\n subscriber,\n });\n }\n }\n }\n }\n\n // Engage the learning loop around run() (opt-in; no-op otherwise). Done\n // here — not only in execute() — so the background/scheduled path, which\n // calls initialize() then run() directly, learns too.\n this.wrapRunForLearning();\n\n return this;\n }\n\n /**\n * Set up signal handlers for graceful shutdown\n * Handles SIGTERM and SIGINT for single-agent processes that explicitly opt in.\n */\n private setupSignalHandlers(): void {\n const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n\n for (const signal of signals) {\n const handler = () => {\n this.logger.info(`Received ${signal}, shutting down gracefully`);\n this.shutdown()\n .then(() => {\n process.exit(0);\n })\n .catch((error) => {\n this.logger.error('Error during shutdown', { error });\n process.exit(1);\n });\n };\n\n this.signalHandlers.set(signal, handler);\n process.on(signal, handler);\n }\n }\n\n /**\n * Migrate legacy simple-name dispatch subscribers to the canonical agent type.\n *\n * Older releases used `this.constructor.name` directly for subscriber IDs.\n * That collides across packages and leaves fan-out dispatches targeted at the\n * wrong subscriber once qualified names are available.\n */\n private async migrateLegacyDispatchSubscriptions(\n dispatch: DispatchBus,\n ): Promise<void> {\n if (!this._db) {\n return;\n }\n\n const legacySubscriber = this.constructor.name;\n const canonicalSubscriber = this.getAgentTypeName();\n\n if (legacySubscriber === canonicalSubscriber) {\n return;\n }\n\n const legacySubscriptions =\n await dispatch.listSubscriptions(legacySubscriber);\n if (legacySubscriptions.length === 0) {\n return;\n }\n\n const currentSubscriptions =\n await dispatch.listSubscriptions(canonicalSubscriber);\n const currentSignalTypes = new Set(\n currentSubscriptions.map((sub) => sub.signalType),\n );\n\n for (const subscription of legacySubscriptions) {\n if (!currentSignalTypes.has(subscription.signalType)) {\n await dispatch.subscribe({\n signalType: subscription.signalType,\n subscriber: canonicalSubscriber,\n handler: subscription.handler,\n delivery: subscription.delivery,\n enabled: subscription.enabled,\n });\n }\n\n await dispatch.unsubscribe(subscription.signalType, legacySubscriber);\n }\n\n // Tenant isolation (S5 #1398): the bus's subscribe/unsubscribe calls above\n // are tenant-scoped server-side, but this raw UPDATE reaches around the bus\n // directly into `_smrt_dispatch`. Without a tenant predicate it would\n // rewrite the target/processor of EVERY tenant's dispatch rows matching the\n // legacy subscriber name, letting an agent under one tenant retarget another\n // tenant's pending dispatches. Derive the active scope server-side (never\n // from caller input) and restrict the UPDATE to the rows the bus would let\n // this scope read/claim.\n const [tenantClause, tenantParams] = buildDispatchTenantUpdatePredicate(\n resolveDispatchTenantScope(),\n );\n\n await this._db.query(\n `UPDATE _smrt_dispatch\n SET target_subscriber = CASE\n WHEN target_subscriber = ? THEN ?\n ELSE target_subscriber\n END,\n processed_by = CASE\n WHEN processed_by = ? THEN ?\n ELSE processed_by\n END\n WHERE (target_subscriber = ? OR processed_by = ?)${tenantClause}`,\n legacySubscriber,\n canonicalSubscriber,\n legacySubscriber,\n canonicalSubscriber,\n legacySubscriber,\n legacySubscriber,\n ...tenantParams,\n );\n }\n\n /**\n * Clean up signal handlers\n */\n private cleanupSignalHandlers(): void {\n for (const [signal, handler] of this.signalHandlers.entries()) {\n process.removeListener(signal, handler);\n }\n this.signalHandlers.clear();\n }\n\n /**\n * Validate configuration and dependencies\n * Override to check agent-specific requirements\n *\n * @throws Error if validation fails\n *\n * @example\n * ```typescript\n * async validate(): Promise<void> {\n * if (!this.config.apiKey) {\n * throw new Error('API key is required');\n * }\n * }\n * ```\n */\n async validate(): Promise<void> {\n this.logger.info('Validating agent configuration');\n // Base implementation - extending agents should override\n }\n\n /**\n * Main agent logic\n * Must be implemented by extending class\n *\n * Update this.lastRun.itemsProcessed to track work done\n *\n * @example\n * ```typescript\n * async run(): Promise<void> {\n * this.logger.info('Starting agent work');\n * let processed = 0;\n *\n * for (const item of items) {\n * await this.processItem(item);\n * processed++;\n * }\n *\n * this.lastRun.itemsProcessed = processed;\n * this.logger.info(`Processed ${processed} items`);\n * }\n * ```\n */\n abstract run(): Promise<void>;\n\n /**\n * Cleanup and shutdown\n * Override to perform graceful shutdown\n *\n * Always call super.shutdown() to clean up signal handlers\n *\n * @example\n * ```typescript\n * async shutdown(): Promise<void> {\n * this.logger.info('Cleaning up resources');\n * await this.cleanup();\n * await super.shutdown();\n * }\n * ```\n */\n async shutdown(): Promise<void> {\n this.status = 'shutdown';\n this.logger.info('Agent shutting down');\n this.cleanupSignalHandlers();\n }\n\n /**\n * Execute agent with lifecycle management\n *\n * Runs the full lifecycle:\n * 1. initialize() — seeds signal subscriptions if declared\n * 2. validate()\n * 3. processDispatches() — auto-processes pending dispatches if subscriptions exist\n * 4. run()\n *\n * Note: handleDispatch() callbacks may fire before run() is entered.\n *\n * On error:\n * 1. Sets status to 'error'\n * 2. Logs error\n * 3. Re-throws error\n *\n * @example\n * ```typescript\n * const agent = new MyAgent({ name: 'my-agent' });\n *\n * try {\n * await agent.execute();\n * console.log('Agent completed successfully');\n * } catch (error) {\n * console.error('Agent failed:', error);\n * }\n * ```\n */\n async execute(): Promise<void> {\n try {\n await this.initialize();\n await this.validate();\n\n this.status = 'running';\n\n // Auto-process pending dispatches for agents with signal subscriptions\n if (this._db) {\n const dispatch = await this.getDispatch();\n const subs = await dispatch.listSubscriptions(\n this.getDispatchSubscriber(),\n );\n if (subs.length > 0) {\n const count = await this.processDispatches();\n if (count > 0) {\n this.logger.info(`Processed ${count} pending dispatches`);\n }\n }\n }\n\n // The learning loop (#1886) is wrapped around run() in initialize(), so\n // recall-before / capture-after fires here and on the scheduled path\n // alike — nothing learning-specific is needed in execute() itself.\n await this.run();\n this.status = 'idle';\n\n this.logger.info('Agent execution completed');\n } catch (error) {\n this.status = 'error';\n this.logger.error('Agent execution failed', { error });\n throw error;\n }\n }\n\n /**\n * Query objects this agent is interested in\n *\n * Returns items from all configured object types, filtered and sorted\n * according to interest configuration. If handlers are defined on filters,\n * they are called for each matched item and the result is included.\n *\n * @returns Array of { type, data, name?, handled? } results\n * @throws Error if no interests are configured\n *\n * @example\n * ```typescript\n * const items = await this.interesting();\n * for (const { type, data, name, handled } of items) {\n * console.log(`Processing ${type} from \"${name}\": action=${handled?.action}`);\n * }\n * ```\n */\n async interesting(): Promise<InterestResult[]> {\n if (!this.interests) {\n throw new Error(\n `Agent ${this.constructor.name} has no interests configured. ` +\n `Set interests in constructor options to use interesting().`,\n );\n }\n\n if (\n !this.interests.objects ||\n Object.keys(this.interests.objects).length === 0\n ) {\n this.logger.warn('Agent has empty interests.objects configuration');\n return [];\n }\n\n const results: InterestResult[] = [];\n\n // Process each object type in interests.objects\n for (const [className, config] of Object.entries(this.interests.objects)) {\n try {\n const items = await this.queryInterestingObjects(className, config);\n results.push(...items);\n } catch (error) {\n // Log warning and continue with other types\n this.logger.warn(`Failed to query ${className} for interests`, {\n error,\n });\n }\n }\n\n // Apply global qualifier if configured\n if (this.interests.qualify) {\n const allItems = results.map((r) => r.data);\n const qualified = await this.interests.qualify(allItems);\n\n // Rebuild results array with only qualified items\n const qualifiedSet = new Set(qualified);\n const filteredResults = results.filter((r) => qualifiedSet.has(r.data));\n\n // Apply global sort if configured\n if (this.interests.sort) {\n return this.sortResults(filteredResults, this.interests.sort);\n }\n return filteredResults;\n }\n\n // Apply global sort if configured (no global qualifier)\n if (this.interests.sort) {\n return this.sortResults(results, this.interests.sort);\n }\n\n return results;\n }\n\n /**\n * Query a single object type based on interest config\n *\n * Supports both single filter and array of filters.\n * Each filter can use standard SDK filters OR custom query function.\n * Returns InterestResult[] with handler results included.\n */\n private async queryInterestingObjects(\n className: string,\n config: ObjectInterestConfig,\n ): Promise<InterestResult[]> {\n // Check if class is registered (case-insensitive)\n if (!ObjectRegistry.hasClass(className)) {\n this.logger.warn(\n `Object type \"${className}\" not found in ObjectRegistry. ` +\n `Skipping in interests query.`,\n );\n return [];\n }\n\n // Get collection for this class type\n const collection = await ObjectRegistry.getCollection(\n className,\n this.options,\n );\n\n // Normalize config to array format\n const filters = this.normalizeInterestConfig(config);\n\n // Query each filter and collect results\n const allResults: InterestResult[] = [];\n\n for (const filter of filters) {\n const items = await this.queryInterestFilter(\n className,\n filter,\n collection,\n );\n\n // Process each item: call handler if defined, build result\n for (const item of items) {\n const result: InterestResult = {\n type: className,\n data: item,\n name: filter.name,\n };\n\n // Call handler if defined and add to result\n if (filter.handler) {\n result.handled = await filter.handler(item, this);\n }\n\n allResults.push(result);\n }\n }\n\n return allResults;\n }\n\n /**\n * Normalize ObjectInterestConfig to array format\n */\n private normalizeInterestConfig(\n config: ObjectInterestConfig,\n ): InterestFilter[] {\n return Array.isArray(config) ? config : [config];\n }\n\n /**\n * Query a single interest filter\n *\n * Uses collection.query() for custom query functions,\n * or collection.list() for standard SDK filters.\n */\n private async queryInterestFilter(\n _className: string,\n filter: InterestFilter,\n collection: SmrtCollection<SmrtObject>,\n ): Promise<SmrtObject[]> {\n // Custom query path - uses collection.query() for raw SQL power\n if (filter.query) {\n let [whereClause, params] = filter.query(collection.tableName);\n\n // Ensure manifest is loaded for this class and its ancestors (Issue #515)\n // This is critical for cross-package STI where getTableStrategy() needs\n // the complete inheritance chain to detect inherited STI configuration\n //\n // We walk the extends chain directly (not using cached getInheritanceChain)\n // to avoid caching an incomplete chain before all manifests are loaded.\n // After loading all ancestors, we invalidate the cache so getTableStrategy\n // rebuilds it with complete data.\n await ObjectRegistry.ensureManifestLoaded(_className);\n let currentClass = ObjectRegistry.getClass(_className);\n while (currentClass?.extends) {\n const parentName = currentClass.extends;\n // Skip framework base classes\n if (\n parentName === 'SmrtObject' ||\n parentName === 'SmrtClass' ||\n parentName === 'SmrtCollection'\n ) {\n break;\n }\n try {\n await ObjectRegistry.ensureManifestLoaded(parentName);\n } catch {\n // Manifest loading can fail for classes not in manifest - continue\n }\n currentClass = ObjectRegistry.getClass(parentName);\n }\n // Invalidate cached chain so getTableStrategy rebuilds with complete data\n ObjectRegistry.invalidateInheritanceCache(_className);\n\n // Add STI discriminator filter if this is an STI child class.\n // R5-canon: `getSTIBase` returns the qualified name; compare\n // against the qualified form of `_className` so a query against\n // an STI BASE doesn't get an unintended `_meta_type` filter that\n // would hide its descendants.\n const tableStrategy = ObjectRegistry.getTableStrategy(_className);\n if (tableStrategy === 'sti') {\n const stiBase = ObjectRegistry.getSTIBase(_className);\n const classInfo = ObjectRegistry.getClass(_className);\n const qualifiedClassName =\n classInfo?.qualifiedName ?? classInfo?.name ?? _className;\n if (\n stiBase &&\n stiBase !== qualifiedClassName &&\n stiBase !== _className\n ) {\n // Get the qualified name for this class (e.g., '@happyvertical/praeco:Meeting')\n // This is what's stored in the _meta_type column in the database\n const metaTypeValue = classInfo?.qualifiedName || _className;\n // Wrap original where clause and add _meta_type filter\n whereClause = `_meta_type = ? AND (${whereClause})`;\n params = [metaTypeValue, ...params];\n }\n }\n\n // Build full SQL query\n let sql = `SELECT * FROM ${collection.tableName} WHERE ${whereClause}`;\n\n // Add ORDER BY if specified.\n // The sort fields are interpolated directly into the SQL string, so\n // validate each field name and direction against the same allowlist\n // collection.list() uses, to prevent SQL injection if filter.sort ever\n // derives from untrusted input.\n if (filter.sort) {\n const sorts = Array.isArray(filter.sort) ? filter.sort : [filter.sort];\n const orderBy = sorts\n .map((item) => {\n const [field, direction = 'ASC'] = item.trim().split(/\\s+/);\n if (!/^[a-zA-Z0-9_]+$/.test(field)) {\n throw new Error(`Invalid field name for ordering: ${field}`);\n }\n const normalizedDirection = direction.toUpperCase();\n if (\n normalizedDirection !== 'ASC' &&\n normalizedDirection !== 'DESC'\n ) {\n throw new Error(\n `Invalid sort direction: ${direction}. Must be ASC or DESC.`,\n );\n }\n return `${field} ${normalizedDirection}`;\n })\n .join(', ');\n sql += ` ORDER BY ${orderBy}`;\n }\n\n // Add LIMIT if specified\n if (filter.limit) {\n sql += ` LIMIT ?`;\n params.push(filter.limit);\n }\n\n // Execute raw query with hydration\n let items = await collection.query(sql, params);\n\n // Apply qualifier if configured\n if (filter.qualify) {\n items = await filter.qualify(items);\n }\n\n return items;\n }\n\n // Standard filter path - uses collection.list() with SDK filters.\n // Layer the per-instance scope (#1890) as the base so multi-instance agents\n // partition what they process; the global then per-object filters layer on\n // top (winning on key collision). Undefined for singletons → unchanged.\n const mergedFilter = mergeFilters(\n mergeFilters(this.instanceInterestFilter(), this.interests?.filter),\n filter.filter,\n );\n\n const queryOptions: {\n where?: Record<string, unknown>;\n orderBy?: string | string[];\n limit?: number;\n } = {};\n\n if (Object.keys(mergedFilter).length > 0) {\n queryOptions.where = mergedFilter;\n }\n if (filter.sort) {\n queryOptions.orderBy = filter.sort;\n }\n if (filter.limit) {\n queryOptions.limit = filter.limit;\n }\n\n // Execute query\n let items = await collection.list(queryOptions);\n\n // Apply object-specific qualifier if configured\n if (filter.qualify) {\n items = await filter.qualify(items);\n }\n\n return items;\n }\n\n /**\n * Sort results by field(s) across all types\n */\n private sortResults(\n results: InterestResult[],\n sort: string | string[],\n ): InterestResult[] {\n const sortFields = normalizeSort(sort);\n if (sortFields.length === 0) return results;\n\n return [...results].sort((a, b) => {\n for (const sortField of sortFields) {\n const [field, direction = 'ASC'] = sortField.trim().split(/\\s+/);\n const aValue = (a.data as unknown as Record<string, string | number>)[\n field\n ];\n const bValue = (b.data as unknown as Record<string, string | number>)[\n field\n ];\n\n let comparison = 0;\n if (aValue < bValue) comparison = -1;\n else if (aValue > bValue) comparison = 1;\n\n if (comparison !== 0) {\n return direction.toUpperCase() === 'DESC' ? -comparison : comparison;\n }\n }\n return 0;\n });\n }\n}\n\n/**\n * Build the SQL tenant predicate (clause + params) for a raw `_smrt_dispatch`\n * write under the active {@link DispatchTenantScope} (S5 #1398).\n *\n * Mirrors core's `pushTenantPredicate` read/claim semantics so a raw migration\n * UPDATE only ever touches the rows the DispatchBus would let this scope\n * read/claim:\n *\n * - tenancy off (`enforced: false`) → no predicate (pre-tenancy behavior).\n * - active tenant `T` → `(tenant_id = ? OR tenant_id IS NULL)` (own + global).\n * - tenancy on, no active tenant → `tenant_id IS NULL` (fail-closed to global).\n *\n * The returned clause is prefixed with ` AND ` (or empty) so it can be appended\n * directly to an existing `WHERE (...)`.\n */\nfunction buildDispatchTenantUpdatePredicate(\n scope: DispatchTenantScope,\n): [clause: string, params: string[]] {\n if (!scope.enforced) {\n return ['', []];\n }\n if (scope.tenantId !== null) {\n return [' AND (tenant_id = ? OR tenant_id IS NULL)', [scope.tenantId]];\n }\n return [' AND tenant_id IS NULL', []];\n}\n","/**\n * Principal-bound, read-only data-surface tools (#2447).\n *\n * This module deliberately does not know how an application discovers or\n * executes a surface. Applications provide a small, server-side catalog and\n * executor; this package supplies the principal, allow-list, catalog/RBAC,\n * tenant, projection, ordering, and result-boundary enforcement around them.\n */\n\nimport { createHash } from 'node:crypto';\nimport type { AITool } from '@happyvertical/ai';\nimport {\n createDataQueryFingerprint,\n DataQueryValidationError,\n DEFAULT_DATA_QUERY_RESULT_BYTES,\n MAX_DATA_QUERY_FILTERS,\n MAX_DATA_QUERY_REQUEST_BYTES,\n normalizeDataQueryRequest,\n normalizeDataQueryResult,\n normalizeDataQuerySchema,\n type SmrtClassOptions,\n} from '@happyvertical/smrt-core';\nimport type {\n DataQueryFieldDescriptor,\n DataQueryRequest,\n DataQueryResult,\n DataQueryRow,\n DataQuerySchema,\n} from '@happyvertical/smrt-types';\nimport type { PrincipalRun } from './execute-as-principal.js';\nimport type { PrincipalTool, PrincipalToolContext } from './invoke-agent.js';\n\nexport const DATA_DISCOVER_TOOL_SLUG = 'data.discover';\nexport const DATA_INSPECT_TOOL_SLUG = 'data.inspect';\nexport const DATA_QUERY_TOOL_SLUG = 'data.query';\n\nexport const DATA_DISCOVER_FUNCTION_NAME = 'data-discover';\nexport const DATA_INSPECT_FUNCTION_NAME = 'data-inspect';\nexport const DATA_QUERY_FUNCTION_NAME = 'data-query';\n\nexport const DEFAULT_DATA_SURFACE_DEADLINE_MS = 5_000;\nexport const MAX_DATA_SURFACE_DEADLINE_MS = 30_000;\n\nexport type DataSurfaceFieldMetadata = Readonly<\n Record<string, string | number | boolean | null>\n>;\n\n/** A data field plus server-owned visibility policy annotations. */\nexport interface DataSurfaceField extends DataQueryFieldDescriptor {\n sensitive?: boolean;\n readPermission?: string;\n metadata?: DataSurfaceFieldMetadata;\n}\n\n/** Server-owned schema; policy annotations never cross the core query boundary. */\nexport interface DataSurfaceSchema extends Omit<DataQuerySchema, 'fields'> {\n fields: DataSurfaceField[];\n}\n\n/** A server-owned data source. Never construct this from model/tool input. */\nexport interface DataSurfaceDefinition {\n /** Stable opaque id presented to the model. */\n id: string;\n /** Permission-catalog collection used for the read gate. */\n collection: string;\n /** Optional backing SMRT class, useful to registry-backed executors. */\n className?: string;\n label?: string;\n description?: string;\n schema: DataSurfaceSchema;\n /** Optional surface-specific executor. */\n execute?: DataSurfaceExecutor;\n}\n\nexport interface DataSurfacePrincipal {\n /** The authenticated execution principal, copied from the live run. */\n userId: string;\n /** The authenticated tenant scope, copied from the live run. */\n tenantId: string | null;\n}\n\nexport interface DataSurfaceExecutionContext {\n run: PrincipalRun;\n principal: DataSurfacePrincipal;\n db?: SmrtClassOptions['db'];\n /** Signal for adapters that can cancel database work. */\n signal: AbortSignal;\n}\n\nexport type DataSurfaceExecutorResult =\n | DataQueryResult\n | DataQueryRow[]\n | {\n rows?: DataQueryRow[];\n total?: DataQueryResult['total'];\n facets?: DataQueryResult['facets'];\n freshness?: DataQueryResult['freshness'];\n warnings?: string[];\n truncated?: boolean;\n nextCursor?: string;\n hasMore?: boolean;\n };\n\nexport type DataSurfaceExecutor = (\n surface: DataSurfaceDefinition,\n request: DataQueryRequest,\n context: DataSurfaceExecutionContext,\n) => Promise<DataSurfaceExecutorResult>;\n\nexport interface DataSurfaceAuditEntry {\n action: 'discover' | 'inspect' | 'query';\n surfaceId?: string;\n requestId?: string;\n userId: string;\n tenantId: string | null;\n rowCount?: number;\n truncated?: boolean;\n}\n\nexport type DataSurfaceAuditSink = (\n entry: DataSurfaceAuditEntry,\n) => void | Promise<void>;\n\ntype DataSurfaceAuditInput = Omit<DataSurfaceAuditEntry, 'userId' | 'tenantId'>;\n\nexport interface DataSurfaceToolsOptions {\n /** Server-owned catalog. A function is evaluated per authenticated run. */\n surfaces:\n | readonly DataSurfaceDefinition[]\n | ((\n run: PrincipalRun,\n ) =>\n | readonly DataSurfaceDefinition[]\n | Promise<readonly DataSurfaceDefinition[]>);\n /** Shared executor used when a definition does not provide one. */\n execute?: DataSurfaceExecutor;\n /** Audit sink for individual tool actions. */\n audit?: DataSurfaceAuditSink;\n /** Deadline for an adapter call. Defaults to five seconds. */\n deadlineMs?: number;\n /** Receives detailed server-side failures; never surfaced to the model. */\n onFailure?: DataSurfaceFailureSink;\n}\n\nexport interface DataSurfaceFailureEntry {\n action: 'discover' | 'inspect' | 'query';\n surfaceId?: string;\n requestId?: string;\n userId: string;\n tenantId: string | null;\n error: unknown;\n}\n\nexport type DataSurfaceFailureSink = (\n entry: DataSurfaceFailureEntry,\n) => void | Promise<void>;\n\nexport class DataSurfaceDeniedError extends Error {\n readonly status = 403;\n\n constructor() {\n // Deliberately generic: callers must not learn whether a surface exists.\n super('Data surface is not available.');\n this.name = 'DataSurfaceDeniedError';\n }\n}\n\nexport class DataSurfaceDeadlineError extends Error {\n readonly status = 504;\n\n constructor() {\n super('Data surface query exceeded its execution deadline.');\n this.name = 'DataSurfaceDeadlineError';\n }\n}\n\n/** Adapter output was not in the requested deterministic order. */\nexport class DataSurfaceResultOrderError extends Error {\n readonly status = 502;\n\n constructor() {\n // Do not include field/row values in the public error.\n super('Data surface returned results in an invalid order.');\n this.name = 'DataSurfaceResultOrderError';\n }\n}\n\n/** Stable public failure for executor and result-boundary errors. */\nexport class DataSurfaceQueryError extends Error {\n readonly status = 502;\n readonly code = 'DATA_SURFACE_QUERY_FAILED';\n\n constructor() {\n super('Data surface query failed.');\n this.name = 'DataSurfaceQueryError';\n }\n}\n\n// Audit failures are reported at the point where the audit sink rejects. Keep\n// the wrapped public error marked so the query boundary does not report it a\n// second time when it unwinds through the outer executor catch.\nconst reportedFailureErrors = new WeakSet<object>();\n\n/** Stable public failure for requests that name hidden schema capabilities. */\nexport class DataSurfaceRequestError extends Error {\n readonly status = 400;\n readonly code = 'DATA_SURFACE_REQUEST_INVALID';\n\n constructor() {\n super('Data surface query request is invalid.');\n this.name = 'DataSurfaceRequestError';\n }\n}\n\nconst HIDDEN_SCHEMA_REQUEST_CODES = new Set([\n 'DATA_QUERY_FIELD_NOT_ALLOWED',\n 'DATA_QUERY_PROJECTION_NOT_ALLOWED',\n 'DATA_QUERY_SORT_NOT_ALLOWED',\n 'DATA_QUERY_FACET_NOT_ALLOWED',\n]);\n\nfunction normalizeSurfaceRequest(\n value: unknown,\n schema: DataQuerySchema,\n): DataQueryRequest {\n try {\n return normalizeDataQueryRequest(value, schema);\n } catch (error) {\n if (\n error instanceof DataQueryValidationError &&\n HIDDEN_SCHEMA_REQUEST_CODES.has(error.code)\n ) {\n throw new DataSurfaceRequestError();\n }\n throw error;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isDataQueryRows(value: unknown[]): value is DataQueryRow[] {\n return value.every(isRecord);\n}\n\nfunction dataQueryRowsOrThrow(value: unknown[]): DataQueryRow[] {\n if (!isDataQueryRows(value)) throw new DataSurfaceQueryError();\n return value;\n}\n\nfunction nonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction principalFromRun(run: PrincipalRun): DataSurfacePrincipal {\n const userId = run.context.userId;\n if (!userId) throw new DataSurfaceDeniedError();\n return { userId, tenantId: run.context.tenantId };\n}\n\nfunction coreSchema(schema: DataSurfaceSchema): DataQuerySchema {\n return {\n ...schema,\n fields: schema.fields.map(\n ({\n sensitive: _sensitive,\n readPermission: _readPermission,\n metadata: _metadata,\n ...field\n }) => field,\n ),\n };\n}\n\nfunction visibleSchema(\n schema: DataSurfaceSchema,\n run: PrincipalRun,\n): DataQuerySchema {\n const fields = schema.fields.filter((field) => {\n if (field.sensitive === true) return false;\n const readPermission = field.readPermission;\n if (readPermission && !run.permissions.includes(readPermission)) {\n return false;\n }\n return true;\n });\n if (!fields.some((field) => field.id === schema.identityField)) {\n throw new DataSurfaceDeniedError();\n }\n return normalizeDataQuerySchema(coreSchema({ ...schema, fields }));\n}\n\nfunction descriptor(surface: DataSurfaceDefinition, schema: DataQuerySchema) {\n return {\n id: surface.id,\n label: surface.label ?? surface.id,\n ...(surface.description ? { description: surface.description } : {}),\n collection: surface.collection,\n identityField: schema.identityField,\n fields: schema.fields.map((field) => ({\n id: field.id,\n type: field.type,\n projectable: field.projectable !== false,\n sortable: field.sortable === true,\n facetable: field.facetable === true,\n filterOperators: [...(field.filterOperators ?? [])].sort(),\n })),\n supports: schema.supports ?? {},\n limits: {\n defaultPageLimit: schema.defaultPageLimit,\n maxPageLimit: schema.maxPageLimit,\n maxResultBytes: schema.maxResultBytes,\n },\n };\n}\n\nasync function availableSurfaces(\n options: DataSurfaceToolsOptions,\n run: PrincipalRun,\n): Promise<Array<{ surface: DataSurfaceDefinition; schema: DataQuerySchema }>> {\n const configured =\n typeof options.surfaces === 'function'\n ? await options.surfaces(run)\n : options.surfaces;\n const result: Array<{\n surface: DataSurfaceDefinition;\n schema: DataQuerySchema;\n }> = [];\n for (const surface of configured) {\n if (\n !surface ||\n !nonEmptyString(surface.id) ||\n !nonEmptyString(surface.collection)\n )\n continue;\n try {\n // A missing catalog permission is intentionally indistinguishable from a\n // missing surface. The allow-list gate is checked before this function.\n await run.assertOperation(surface.collection, 'read');\n result.push({ surface, schema: visibleSchema(surface.schema, run) });\n } catch {\n // Do not leak unauthorized surface ids, schemas, or permission errors.\n }\n }\n return result.sort((left, right) =>\n left.surface.id === right.surface.id\n ? 0\n : left.surface.id < right.surface.id\n ? -1\n : 1,\n );\n}\n\nfunction findSurface(\n surfaces: Array<{ surface: DataSurfaceDefinition; schema: DataQuerySchema }>,\n id: unknown,\n) {\n return surfaces.find((entry) => entry.surface.id === id);\n}\n\nfunction sortRows(\n rows: DataQueryRow[],\n request: DataQueryRequest,\n schema: DataQuerySchema,\n): DataQueryRow[] {\n const terms = request.sort ?? [];\n return [...rows].sort((left, right) =>\n compareRows(left, right, terms, schema),\n );\n}\n\nfunction compareDataValues(\n left: unknown,\n right: unknown,\n type: DataQuerySchema['fields'][number]['type'],\n): number {\n if (left === right) return 0;\n if (left === null || left === undefined) return -1;\n if (right === null || right === undefined) return 1;\n if (type === 'number') return Number(left) - Number(right);\n if (type === 'datetime') {\n const leftTime = Date.parse(String(left));\n const rightTime = Date.parse(String(right));\n if (Number.isFinite(leftTime) && Number.isFinite(rightTime)) {\n return leftTime - rightTime;\n }\n }\n if (type === 'boolean') return Number(Boolean(left)) - Number(Boolean(right));\n const leftString = String(left);\n const rightString = String(right);\n return leftString === rightString ? 0 : leftString < rightString ? -1 : 1;\n}\n\nfunction compareRows(\n left: DataQueryRow,\n right: DataQueryRow,\n terms: readonly NonNullable<DataQueryRequest['sort']>[number][],\n schema: DataQuerySchema,\n): number {\n for (const term of terms) {\n const type =\n schema.fields.find((field) => field.id === term.field)?.type ?? 'string';\n const result = compareDataValues(left[term.field], right[term.field], type);\n if (result !== 0) return term.direction === 'desc' ? -result : result;\n }\n const identityType =\n schema.fields.find((field) => field.id === schema.identityField)?.type ??\n 'string';\n return compareDataValues(\n left[schema.identityField],\n right[schema.identityField],\n identityType,\n );\n}\n\nfunction isCanonicalOrder(\n rows: DataQueryRow[],\n request: DataQueryRequest,\n schema: DataQuerySchema,\n): boolean {\n const terms = request.sort ?? [];\n for (let index = 1; index < rows.length; index += 1) {\n if (compareRows(rows[index - 1], rows[index], terms, schema) > 0) {\n return false;\n }\n }\n return true;\n}\n\nfunction projectionForResult(request: DataQueryRequest): string[] {\n return request.projection ?? [];\n}\n\nfunction externalValidationRequest(\n request: DataQueryRequest,\n schema: DataQuerySchema,\n): DataQueryRequest {\n if (\n request.mode !== 'rows' ||\n !request.projection ||\n request.projection.length <= MAX_DATA_QUERY_FILTERS\n ) {\n return request;\n }\n return {\n ...request,\n projection: request.projection.filter(\n (field) => field !== schema.identityField,\n ),\n };\n}\n\nfunction canonicalRequestValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(canonicalRequestValue);\n if (isRecord(value)) {\n return Object.fromEntries(\n Object.keys(value)\n .sort()\n .map((key) => [key, canonicalRequestValue(value[key])]),\n );\n }\n return value;\n}\n\n/**\n * Create the fingerprint for the already-normalized request passed to a\n * surface executor. This supports internal projections beyond core's public\n * 50-field projection limit; callers must use the exact request received.\n */\nexport function createDataSurfaceQueryFingerprint(\n request: DataQueryRequest,\n): string {\n const { requestId: _requestId, page: _page, ...semanticQuery } = request;\n return `dq1_${createHash('sha256')\n .update(JSON.stringify(canonicalRequestValue(semanticQuery)))\n .digest('base64url')}`;\n}\n\nfunction shorthandResultCandidate(\n request: DataQueryRequest,\n schema: DataQuerySchema,\n rawRecord: Record<string, unknown> | undefined,\n rows: readonly unknown[],\n): Record<string, unknown> {\n const rawPage = isRecord(rawRecord?.page) ? rawRecord.page : undefined;\n const explicitHasMore =\n typeof rawPage?.hasMore === 'boolean'\n ? rawPage.hasMore\n : typeof rawRecord?.hasMore === 'boolean'\n ? rawRecord.hasMore\n : undefined;\n const nextCursor =\n typeof rawPage?.nextCursor === 'string'\n ? rawPage.nextCursor\n : typeof rawRecord?.nextCursor === 'string'\n ? rawRecord.nextCursor\n : undefined;\n if (\n request.page &&\n rows.length === request.page.limit &&\n explicitHasMore === undefined &&\n !nextCursor\n ) {\n // An exact-limit shorthand page may have more rows. Require the adapter\n // to provide continuation metadata instead of falsely declaring a final\n // page and silently truncating a result set.\n throw new DataSurfaceQueryError();\n }\n return {\n version: 1,\n requestId: request.requestId,\n queryFingerprint: createDataQueryFingerprint(request, schema),\n identityField: schema.identityField,\n rows,\n ...(request.page\n ? {\n page:\n request.page.kind === 'offset'\n ? {\n kind: 'offset',\n offset: request.page.offset,\n limit: request.page.limit,\n hasMore: explicitHasMore ?? Boolean(nextCursor),\n }\n : {\n kind: 'cursor',\n limit: request.page.limit,\n hasMore: explicitHasMore ?? Boolean(nextCursor),\n ...(nextCursor ? { nextCursor } : {}),\n },\n }\n : {}),\n total: rawRecord?.total ?? { kind: 'unavailable' },\n ...(rawRecord?.facets ? { facets: rawRecord.facets } : {}),\n freshness: rawRecord?.freshness ?? { state: 'unknown' },\n warnings: Array.isArray(rawRecord?.warnings) ? rawRecord.warnings : [],\n truncated: rawRecord?.truncated === true,\n };\n}\n\nfunction normalizeWideRows(\n rawRecord: Record<string, unknown> | undefined,\n rawRows: unknown[],\n request: DataQueryRequest,\n resultRequest: DataQueryRequest,\n schema: DataQuerySchema,\n internal: { request: DataQueryRequest; schema: DataQuerySchema },\n): DataQueryResult {\n const hasVersionedResult =\n rawRecord !== undefined && Object.hasOwn(rawRecord, 'version');\n if (\n hasVersionedResult &&\n (rawRecord.version !== 1 ||\n rawRecord.requestId !== internal.request.requestId ||\n rawRecord.identityField !== internal.schema.identityField ||\n rawRecord.queryFingerprint !==\n createDataSurfaceQueryFingerprint(internal.request))\n ) {\n throw new DataSurfaceQueryError();\n }\n const requestedFields = resultRequest.projection ?? [schema.identityField];\n const sortOnlyFields = new Set(\n (request.sort ?? [])\n .map((term) => term.field)\n .filter((field) => !requestedFields.includes(field)),\n );\n const chunkSize = MAX_DATA_QUERY_FILTERS - 1;\n const chunks: DataQueryResult[] = [];\n for (let offset = 0; offset < requestedFields.length; offset += chunkSize) {\n const fields = requestedFields.slice(offset, offset + chunkSize);\n const allowedFields = new Set([\n schema.identityField,\n ...requestedFields,\n ...sortOnlyFields,\n ]);\n const chunkFields = new Set([schema.identityField, ...fields]);\n const chunkRows = rawRows.map((row) => {\n if (!isRecord(row)) return row;\n if (Object.keys(row).some((field) => !allowedFields.has(field))) {\n return row;\n }\n return Object.fromEntries(\n Object.entries(row).filter(([field]) => chunkFields.has(field)),\n );\n });\n // Chunk validation checks field values and page bounds; ordering is\n // validated separately against the complete internal request below.\n const chunkRequest = { ...resultRequest, projection: fields, sort: [] };\n // Correlation fields on a versioned result are checked above before this\n // per-chunk validation envelope is constructed. The chunk fingerprint\n // is necessarily different from the full internal projection's\n // fingerprint because core's normalizer has a 50-field projection cap.\n const candidate = hasVersionedResult\n ? {\n ...rawRecord,\n requestId: chunkRequest.requestId,\n queryFingerprint: createDataQueryFingerprint(chunkRequest, schema),\n identityField: schema.identityField,\n rows: chunkRows,\n }\n : shorthandResultCandidate(chunkRequest, schema, rawRecord, chunkRows);\n chunks.push(normalizeDataQueryResult(candidate, chunkRequest, schema));\n }\n if (chunks.length === 0) {\n throw new DataSurfaceQueryError();\n }\n const rows = chunks[0].rows.map((_, index) =>\n Object.assign({}, ...chunks.map((chunk) => chunk.rows[index])),\n );\n const result = {\n ...chunks[0],\n requestId: request.requestId,\n queryFingerprint: createDataSurfaceQueryFingerprint(request),\n identityField: schema.identityField,\n rows,\n };\n const bytes = new TextEncoder().encode(JSON.stringify(result)).byteLength;\n if (bytes > (schema.maxResultBytes ?? DEFAULT_DATA_QUERY_RESULT_BYTES)) {\n throw new DataSurfaceQueryError();\n }\n return result;\n}\n\nfunction addSortOnlyValues(\n rows: DataQueryRow[],\n rawRows: DataQueryRow[],\n request: DataQueryRequest,\n internalSchema: DataQuerySchema,\n rawRecord: Record<string, unknown> | undefined,\n): DataQueryRow[] {\n const projection = new Set(\n request.projection ?? [internalSchema.identityField],\n );\n const sortOnly = (request.sort ?? [])\n .map((term) => term.field)\n .filter((field) => !projection.has(field));\n if (sortOnly.length === 0) return rows;\n const chunkSize = MAX_DATA_QUERY_FILTERS - 1;\n const validatedChunks: DataQueryResult[] = [];\n for (let offset = 0; offset < sortOnly.length; offset += chunkSize) {\n const fields = sortOnly.slice(offset, offset + chunkSize);\n const validationProjection = [\n ...new Set([internalSchema.identityField, ...fields]),\n ].sort();\n const validationRequest = {\n ...request,\n projection: validationProjection,\n sort: [],\n };\n const validationRows = rawRows.map((row) =>\n Object.fromEntries(\n Object.entries(row).filter(([field]) =>\n validationProjection.includes(field),\n ),\n ),\n );\n validatedChunks.push(\n normalizeDataQueryResult(\n shorthandResultCandidate(\n validationRequest,\n internalSchema,\n rawRecord,\n validationRows,\n ),\n validationRequest,\n internalSchema,\n ),\n );\n }\n return rows.map((row, index) => {\n const result = { ...row };\n for (const field of sortOnly) {\n for (const chunk of validatedChunks) {\n const validatedRow = chunk.rows[index];\n if (Object.hasOwn(validatedRow, field)) {\n result[field] = validatedRow[field];\n break;\n }\n }\n }\n return result;\n });\n}\n\nfunction buildInternalQuery(\n request: DataQueryRequest,\n schema: DataQuerySchema,\n): { request: DataQueryRequest; schema: DataQuerySchema } {\n const sort = request.sort ?? [];\n if (request.mode !== 'rows' || sort.length === 0) {\n return { request, schema };\n }\n const sortFields = new Set(sort.map((term) => term.field));\n const internalSchema = {\n ...schema,\n fields: schema.fields.map((field) =>\n sortFields.has(field.id) ? { ...field, projectable: true } : field,\n ),\n };\n const projection = [\n ...new Set([\n ...(request.projection ?? [schema.identityField]),\n ...sortFields,\n ]),\n ].sort();\n const internalRequest = { ...request, projection };\n const requestBytes = new TextEncoder().encode(\n JSON.stringify(internalRequest),\n ).byteLength;\n if (requestBytes > MAX_DATA_QUERY_REQUEST_BYTES) {\n throw new DataSurfaceQueryError();\n }\n return {\n schema: internalSchema,\n request: internalRequest,\n };\n}\n\nfunction requireSortValues(\n rows: DataQueryRow[],\n request: DataQueryRequest,\n): void {\n for (const row of rows) {\n for (const term of request.sort ?? []) {\n if (!Object.hasOwn(row, term.field)) {\n throw new DataSurfaceResultOrderError();\n }\n }\n }\n}\n\nfunction stripInternalProjection(\n rows: DataQueryRow[],\n request: DataQueryRequest,\n): DataQueryRow[] {\n const projection = projectionForResult(request).filter(Boolean);\n return rows.map((row) =>\n Object.fromEntries(\n projection\n .filter((field) => Object.hasOwn(row, field))\n .map((field) => [field, row[field]]),\n ),\n );\n}\n\nasync function reportFailure(\n options: DataSurfaceToolsOptions,\n run: PrincipalRun,\n action: DataSurfaceFailureEntry['action'],\n surfaceId: string | undefined,\n requestId: string | undefined,\n error: unknown,\n): Promise<void> {\n try {\n const principal = principalFromRun(run);\n await options.onFailure?.({\n action,\n ...(surfaceId !== undefined ? { surfaceId } : {}),\n requestId,\n ...principal,\n error,\n });\n } catch {\n // Failure telemetry must never alter the stable public error contract.\n }\n}\n\nasync function bounded<T>(\n promise: Promise<T>,\n deadlineMs: number,\n controller: AbortController,\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n // Adapters may observe this signal and cancel their database request.\n controller.abort();\n reject(new DataSurfaceDeadlineError());\n }, deadlineMs);\n });\n const abort = new Promise<never>((_, reject) => {\n controller.signal.addEventListener(\n 'abort',\n () => reject(new DataSurfaceDeadlineError()),\n { once: true },\n );\n });\n try {\n return await Promise.race([promise, timeout, abort]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nfunction requestFromArgs(args: Record<string, unknown>): unknown {\n return args.request ?? args;\n}\n\nfunction tool(\n slug: string,\n functionName: string,\n description: string,\n parameters: Record<string, unknown>,\n execute: (context: PrincipalToolContext) => Promise<unknown>,\n): PrincipalTool {\n const aiTool: AITool = {\n type: 'function',\n function: { name: functionName, description, parameters },\n };\n return { slug, aiTool, execute };\n}\n\n/** Build the discover/inspect/query tools for a persona conversation. */\nexport function createDataSurfaceTools(\n options: DataSurfaceToolsOptions,\n): PrincipalTool[] {\n const deadlineMs = Math.min(\n Math.max(options.deadlineMs ?? DEFAULT_DATA_SURFACE_DEADLINE_MS, 1),\n MAX_DATA_SURFACE_DEADLINE_MS,\n );\n const audit = async (\n entry: DataSurfaceAuditInput,\n run: PrincipalRun,\n ): Promise<void> => {\n try {\n const principal = principalFromRun(run);\n await options.audit?.({ ...entry, ...principal });\n } catch (error) {\n await reportFailure(\n options,\n run,\n entry.action,\n entry.surfaceId,\n entry.requestId,\n error,\n );\n const publicError = new DataSurfaceQueryError();\n reportedFailureErrors.add(publicError);\n throw publicError;\n }\n };\n const catalog = async (\n run: PrincipalRun,\n action: DataSurfaceFailureEntry['action'],\n ) => {\n try {\n return await availableSurfaces(options, run);\n } catch (error) {\n await reportFailure(options, run, action, undefined, undefined, error);\n throw new DataSurfaceQueryError();\n }\n };\n\n const discover = tool(\n DATA_DISCOVER_TOOL_SLUG,\n DATA_DISCOVER_FUNCTION_NAME,\n 'List data surfaces and their safe, readable fields.',\n { type: 'object', properties: {}, additionalProperties: false },\n async ({ run }) => {\n run.assertToolAllowed(DATA_DISCOVER_TOOL_SLUG);\n const entries = await catalog(run, 'discover');\n await audit({ action: 'discover' }, run);\n return entries.map(({ surface, schema }) => descriptor(surface, schema));\n },\n );\n\n const inspect = tool(\n DATA_INSPECT_TOOL_SLUG,\n DATA_INSPECT_FUNCTION_NAME,\n 'Inspect one readable data surface schema.',\n {\n type: 'object',\n required: ['surfaceId'],\n properties: { surfaceId: { type: 'string' } },\n additionalProperties: false,\n },\n async ({ run, args }) => {\n run.assertToolAllowed(DATA_INSPECT_TOOL_SLUG);\n const entry = findSurface(await catalog(run, 'inspect'), args.surfaceId);\n if (!entry) throw new DataSurfaceDeniedError();\n await audit({ action: 'inspect', surfaceId: entry.surface.id }, run);\n return descriptor(entry.surface, entry.schema);\n },\n );\n\n const query = tool(\n DATA_QUERY_TOOL_SLUG,\n DATA_QUERY_FUNCTION_NAME,\n 'Run a bounded read query against one readable data surface.',\n {\n type: 'object',\n required: ['surfaceId', 'request'],\n properties: {\n surfaceId: { type: 'string' },\n request: { type: 'object' },\n },\n additionalProperties: false,\n },\n async ({ run, args, db }) => {\n run.assertToolAllowed(DATA_QUERY_TOOL_SLUG);\n const entry = findSurface(await catalog(run, 'query'), args.surfaceId);\n if (!entry) throw new DataSurfaceDeniedError();\n const request = normalizeSurfaceRequest(\n requestFromArgs(args),\n entry.schema,\n );\n const principal = principalFromRun(run);\n const signal = new AbortController();\n const executor = entry.surface.execute ?? options.execute;\n if (!executor) throw new DataSurfaceDeniedError();\n try {\n const internal = buildInternalQuery(request, entry.schema);\n const raw = await bounded(\n executor(entry.surface, internal.request, {\n run,\n principal,\n db: run.context.database ?? db,\n signal: signal.signal,\n }),\n deadlineMs,\n signal,\n );\n const rawRecord = isRecord(raw) ? raw : undefined;\n const rawRows = Array.isArray(raw)\n ? raw\n : rawRecord && Array.isArray(rawRecord.rows)\n ? rawRecord.rows\n : [];\n if (\n request.mode === 'rows' &&\n request.page &&\n rawRows.length > request.page.limit\n ) {\n throw new DataSurfaceQueryError();\n }\n const resultRequest = externalValidationRequest(request, entry.schema);\n const hasVersionedResult =\n rawRecord && Object.hasOwn(rawRecord, 'version');\n const canValidateInternal =\n (internal.request.projection?.length ?? 0) <= MAX_DATA_QUERY_FILTERS;\n const validated = canValidateInternal\n ? normalizeDataQueryResult(\n hasVersionedResult\n ? raw\n : shorthandResultCandidate(\n internal.request,\n internal.schema,\n rawRecord,\n rawRows,\n ),\n internal.request,\n internal.schema,\n )\n : normalizeWideRows(\n rawRecord,\n rawRows,\n request,\n resultRequest,\n entry.schema,\n internal,\n );\n const rawOrderRows =\n request.mode === 'rows' && !canValidateInternal\n ? dataQueryRowsOrThrow(rawRows)\n : validated.rows;\n const orderRows =\n request.mode === 'rows' && !canValidateInternal\n ? addSortOnlyValues(\n validated.rows,\n rawOrderRows,\n request,\n internal.schema,\n rawRecord,\n )\n : rawOrderRows;\n if (request.mode === 'rows') {\n requireSortValues(orderRows, internal.request);\n }\n const orderedRows =\n request.mode === 'rows' && request.page === undefined\n ? sortRows(orderRows, internal.request, internal.schema)\n : orderRows;\n if (\n request.mode === 'rows' &&\n request.page !== undefined &&\n !isCanonicalOrder(orderedRows, internal.request, internal.schema)\n ) {\n throw new DataSurfaceResultOrderError();\n }\n const resultCandidate = {\n ...validated,\n requestId: resultRequest.requestId,\n queryFingerprint: canValidateInternal\n ? createDataQueryFingerprint(resultRequest, entry.schema)\n : createDataSurfaceQueryFingerprint(request),\n identityField: entry.schema.identityField,\n rows: stripInternalProjection(orderedRows, request),\n };\n const result: DataQueryResult = canValidateInternal\n ? normalizeDataQueryResult(\n resultCandidate,\n resultRequest,\n entry.schema,\n )\n : {\n ...resultCandidate,\n queryFingerprint: createDataSurfaceQueryFingerprint(request),\n };\n await audit(\n {\n action: 'query',\n surfaceId: entry.surface.id,\n requestId: result.requestId,\n rowCount: result.rows.length,\n truncated: result.truncated,\n },\n run,\n );\n return result;\n } catch (error) {\n const alreadyReported =\n (typeof error === 'object' && error !== null) ||\n typeof error === 'function'\n ? reportedFailureErrors.has(error)\n : false;\n if (!alreadyReported) {\n await reportFailure(\n options,\n run,\n 'query',\n entry.surface.id,\n request.requestId,\n error,\n );\n }\n if (\n error instanceof DataSurfaceDeadlineError ||\n error instanceof DataSurfaceResultOrderError ||\n error instanceof DataSurfaceQueryError\n ) {\n throw error;\n }\n throw new DataSurfaceQueryError();\n }\n },\n );\n\n return [discover, inspect, query];\n}\n","/**\n * Delegation envelope — the immutable principal + bounded depth carried along an\n * agent-orchestration chain (L3 of the learning-agents epic, #1892).\n *\n * When a conversational (orchestrator) agent invokes a worker agent, and that\n * worker in turn invokes a further worker, the whole chain must run as **one**\n * principal — the originating user — and can never widen it. This module is the\n * pure value object that encodes that invariant:\n *\n * - **Principal immutability.** `runAsUserId`, `tenantId`, and the originating\n * `onBehalfOfUserId` are copied verbatim from parent to child by\n * {@link deriveDelegationEnvelope}; there is no parameter to change them. A\n * caller that *requests* a different principal (e.g. a compromised worker\n * passing `runAsUserId` through the invoke-agent tool) is rejected by\n * {@link assertPrincipalNotWidened} — the request is honoured only when it\n * exactly equals the parent principal.\n * - **Bounded depth.** Every derivation increments `depth` and asserts it stays\n * within {@link MAX_DELEGATION_DEPTH}, so an orchestration chain (or an\n * accidental invoke-yourself loop) can never recurse without limit.\n *\n * The envelope carries no authority of its own: the actual permission bound is\n * still the originating user's live RBAC, enforced when the worker runs via\n * `executeAsPrincipal` (Postgres RLS, or the catalog assert on RLS-off\n * adapters). The envelope only guarantees *which* principal that is and *how\n * deep* the chain may go.\n *\n * @module\n */\n\n/**\n * Maximum delegation depth for an orchestration chain. The orchestrator's own\n * conversation is depth `0`; the first worker it invokes is depth `1`. A worker\n * may invoke a further worker only while the resulting child depth stays within\n * this ceiling, so a chain is at most `MAX_DELEGATION_DEPTH` workers long.\n */\nexport const MAX_DELEGATION_DEPTH = 3;\n\n/**\n * The immutable principal + bounded depth carried from an orchestrator to a\n * worker (and along any further delegation). Serializable, so it can travel in a\n * job's args or a DispatchBus payload to a worker running out of process.\n */\nexport interface DelegationEnvelope {\n /**\n * The user whose live permissions bound the worker's execution. Immutable\n * along the chain — copied verbatim from parent to child.\n */\n runAsUserId: string;\n /** Tenant the principal acts within. Immutable along the chain. */\n tenantId: string | null;\n /**\n * The originating user the whole chain acts **on behalf of** (audited). This\n * is the human who started the conversation; it never changes as delegation\n * deepens, so every action along the chain audits back to the same person.\n */\n onBehalfOfUserId: string;\n /**\n * Current delegation depth. `0` for the orchestrator, `1` for its first\n * worker, and so on — bounded by {@link MAX_DELEGATION_DEPTH}.\n */\n depth: number;\n /**\n * Correlation id linking a worker invocation to the completion dispatch it\n * emits, so the orchestrator can surface the result back into the\n * conversation.\n */\n correlationId: string;\n /**\n * The worker's tool ceiling (its persona's `allowedTools`), carried so a\n * worker that itself runs a tool loop is bounded fail-closed. `undefined`\n * normalizes to \"no tools\" at `executeAsPrincipal` — it never widens.\n */\n allowedTools?: string[];\n}\n\n/**\n * The principal fields a caller may *request* when deriving a child envelope.\n * Any field that is provided must equal the parent's, or\n * {@link assertPrincipalNotWidened} throws — the principal can only ever be\n * inherited, never changed.\n */\nexport interface RequestedPrincipal {\n runAsUserId?: string;\n tenantId?: string | null;\n onBehalfOfUserId?: string;\n}\n\n/**\n * Thrown when a delegation would exceed {@link MAX_DELEGATION_DEPTH}.\n */\nexport class DelegationDepthExceededError extends Error {\n readonly depth: number;\n readonly maxDepth: number;\n readonly status = 400;\n\n constructor(depth: number, maxDepth: number) {\n super(\n `Delegation depth ${depth} exceeds the maximum of ${maxDepth}; ` +\n 'a worker cannot invoke a further worker beyond this depth.',\n );\n this.name = 'DelegationDepthExceededError';\n this.depth = depth;\n this.maxDepth = maxDepth;\n }\n}\n\n/**\n * Thrown when a delegation would *widen* the principal — i.e. a caller requests\n * a `runAsUserId` / `tenantId` / `onBehalfOfUserId` that differs from the\n * parent's. The principal is immutable along an orchestration chain.\n */\nexport class PrincipalWideningError extends Error {\n readonly field: keyof RequestedPrincipal;\n readonly status = 403;\n\n constructor(\n field: keyof RequestedPrincipal,\n expected: unknown,\n got: unknown,\n ) {\n super(\n `Delegation cannot widen the principal: '${String(field)}' is immutable ` +\n `along the chain (bound to ${JSON.stringify(expected)}, ` +\n `refusing ${JSON.stringify(got)}).`,\n );\n this.name = 'PrincipalWideningError';\n this.field = field;\n }\n}\n\n/**\n * Assert a delegation depth is a valid, in-bounds depth.\n *\n * Rejects a non-integer, negative, or non-finite depth as well as one past the\n * ceiling. This matters for the untrusted-payload path: an envelope\n * reconstructed from a persisted dispatch/job could carry `NaN`, a negative, or\n * a string-coerced value, and `NaN > maxDepth` is `false` — so a bare\n * upper-bound check would let it silently bypass the bound and make delegation\n * effectively unbounded.\n *\n * @throws {@link DelegationDepthExceededError} when `depth` is not an integer in `[0, maxDepth]`.\n */\nexport function assertWithinDelegationDepth(\n depth: number,\n maxDepth: number = MAX_DELEGATION_DEPTH,\n): void {\n if (!Number.isInteger(depth) || depth < 0 || depth > maxDepth) {\n throw new DelegationDepthExceededError(depth, maxDepth);\n }\n}\n\n/**\n * Assert a *requested* principal does not widen the parent's.\n *\n * Each provided field must exactly equal the parent's; a mismatch throws\n * {@link PrincipalWideningError}. Omitted fields are fine — they inherit. This\n * is the defence-in-depth guard for the case where an envelope is reconstructed\n * from an untrusted source (a worker's invoke-agent arguments, a job payload):\n * the principal is only ever accepted when it matches, so it can never expand.\n */\nexport function assertPrincipalNotWidened(\n parent: Pick<\n DelegationEnvelope,\n 'runAsUserId' | 'tenantId' | 'onBehalfOfUserId'\n >,\n requested: RequestedPrincipal,\n): void {\n if (\n requested.runAsUserId !== undefined &&\n requested.runAsUserId !== parent.runAsUserId\n ) {\n throw new PrincipalWideningError(\n 'runAsUserId',\n parent.runAsUserId,\n requested.runAsUserId,\n );\n }\n if (\n requested.tenantId !== undefined &&\n requested.tenantId !== parent.tenantId\n ) {\n throw new PrincipalWideningError(\n 'tenantId',\n parent.tenantId,\n requested.tenantId,\n );\n }\n if (\n requested.onBehalfOfUserId !== undefined &&\n requested.onBehalfOfUserId !== parent.onBehalfOfUserId\n ) {\n throw new PrincipalWideningError(\n 'onBehalfOfUserId',\n parent.onBehalfOfUserId,\n requested.onBehalfOfUserId,\n );\n }\n}\n\n/**\n * Options for {@link rootDelegationEnvelope}.\n */\nexport interface RootDelegationEnvelopeOptions {\n /** The principal the orchestrator (and thus the whole chain) runs as. */\n runAsUserId: string;\n /** Tenant the principal acts within. */\n tenantId: string | null;\n /**\n * The originating user the chain acts on behalf of. Defaults to\n * `runAsUserId` when the orchestrator is itself operating directly.\n */\n onBehalfOfUserId?: string;\n /** Correlation id. A fresh UUID is generated when omitted. */\n correlationId?: string;\n /**\n * The orchestrator's own tool ceiling. Carried for completeness; workers do\n * **not** inherit it — a worker's ceiling comes from trusted per-worker policy\n * (`resolveWorkerAllowedTools`) and is fail-closed (no tools) when absent.\n */\n allowedTools?: string[];\n}\n\n/**\n * Build the depth-`0` (orchestrator) envelope that seeds an orchestration chain.\n *\n * The orchestrator's conversation is depth `0`; {@link deriveDelegationEnvelope}\n * produces the depth-`1` envelope for the first worker it invokes.\n */\nexport function rootDelegationEnvelope(\n options: RootDelegationEnvelopeOptions,\n): DelegationEnvelope {\n return {\n runAsUserId: options.runAsUserId,\n tenantId: options.tenantId,\n onBehalfOfUserId: options.onBehalfOfUserId ?? options.runAsUserId,\n depth: 0,\n correlationId: options.correlationId ?? crypto.randomUUID(),\n allowedTools: options.allowedTools,\n };\n}\n\n/**\n * Options for {@link deriveDelegationEnvelope}.\n */\nexport interface DeriveDelegationEnvelopeOptions {\n /** Correlation id for the child invocation. A fresh UUID when omitted. */\n correlationId?: string;\n /**\n * The invoked worker's tool ceiling. When omitted the child carries no tools\n * (fail-closed); it is **not** inherited from the parent so a worker never\n * silently gains the orchestrator's tools.\n */\n allowedTools?: string[];\n /**\n * A principal a caller is *requesting* the child run as. Accepted only when it\n * matches the parent principal exactly (see {@link assertPrincipalNotWidened});\n * otherwise {@link PrincipalWideningError} is thrown. Omit to inherit.\n */\n requestedPrincipal?: RequestedPrincipal;\n /** Depth ceiling override (mainly for tests). */\n maxDepth?: number;\n}\n\n/**\n * Derive the child envelope for a worker invoked by the holder of `parent`.\n *\n * The child **inherits the parent's principal verbatim** (`runAsUserId`,\n * `tenantId`, `onBehalfOfUserId`) — there is no way to change it — increments\n * the depth (asserting the ceiling), and carries the invoked worker's own tool\n * ceiling. A `requestedPrincipal` that differs from the parent's is rejected, so\n * a worker can never invoke a further worker under a broader principal.\n *\n * @throws {@link DelegationDepthExceededError} when the child would exceed the depth ceiling.\n * @throws {@link PrincipalWideningError} when a requested principal widens the parent's.\n */\nexport function deriveDelegationEnvelope(\n parent: DelegationEnvelope,\n options: DeriveDelegationEnvelopeOptions = {},\n): DelegationEnvelope {\n const depth = parent.depth + 1;\n assertWithinDelegationDepth(depth, options.maxDepth);\n if (options.requestedPrincipal) {\n assertPrincipalNotWidened(parent, options.requestedPrincipal);\n }\n return {\n // Principal is copied verbatim — immutable along the chain.\n runAsUserId: parent.runAsUserId,\n tenantId: parent.tenantId,\n onBehalfOfUserId: parent.onBehalfOfUserId,\n depth,\n correlationId: options.correlationId ?? crypto.randomUUID(),\n allowedTools: options.allowedTools,\n };\n}\n","/**\n * invoke-agent — agent orchestration via principal delegation (L3 of the\n * learning-agents epic, #1892).\n *\n * A conversational (orchestrator) agent invokes a worker agent through a\n * standard **`invoke-agent` tool** — gated by the persona's `allowedTools` like\n * any other tool. The tool hands the work to a worker **under the orchestrator's\n * own principal** (`runAsUserId` + `tenantId`), the worker runs via\n * {@link executeAsPrincipal} under that same principal, and reports completion\n * back into the conversation via a **correlated completion dispatch**.\n *\n * This is deliberately **not a new engine**. It is the `invoke-agent` tool plus\n * a completion-dispatch convention on top of the machinery that already ships:\n *\n * - {@link executeAsPrincipal} runs the worker as the delegated principal, so\n * the worker's authority is the originating user's live RBAC — never the\n * worker's own — and every action audits on-behalf-of that user.\n * - The {@link DelegationEnvelope} makes the principal **immutable along the\n * chain** (a worker cannot invoke a further worker under a broader principal)\n * and **bounds delegation depth**.\n * - The DispatchBus carries both the (optional) async invoke signal and the\n * correlated completion, so a worker's result can be surfaced back into the\n * conversation.\n *\n * The **transport is pluggable**. The default {@link inlineInvokeAgentTransport}\n * runs the worker in-process and returns the completion as the tool observation\n * (so it surfaces in the same turn). {@link createDispatchInvokeTransport} emits\n * a DispatchBus `agent.invoke` signal a worker processes out of band\n * ({@link processAgentInvocations}); a job-queue transport (enqueue on the\n * `agents` queue) is a consumer-supplied `InvokeAgentTransport` — orchestration\n * never hard-depends on `@happyvertical/smrt-jobs`, which sits *below* agents in\n * the dependency graph.\n *\n * @module\n */\n\nimport type { AITool } from '@happyvertical/ai';\nimport { createLogger, type Logger } from '@happyvertical/logger';\nimport type { DispatchBus, SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n assertWithinDelegationDepth,\n type DelegationEnvelope,\n deriveDelegationEnvelope,\n} from './delegation.js';\nimport {\n executeAsPrincipal,\n type PrincipalAuditSink,\n type PrincipalRun,\n} from './execute-as-principal.js';\n\n/** Catalog slug + permission id of the standard invoke-agent tool. */\nexport const INVOKE_AGENT_TOOL_SLUG = 'agents.invoke';\n\n/**\n * Provider-safe function name the model receives for the invoke-agent tool.\n * Catalog slugs contain a `.` which some providers (OpenAI) reject in function\n * names; the loop resolves a call by either the slug or this name.\n */\nexport const INVOKE_AGENT_FUNCTION_NAME = 'agents-invoke';\n\n/** DispatchBus signal prefix a worker is invoked through in the async transport. */\nexport const AGENT_INVOKE_SIGNAL = 'agent.invoke';\n\n/** DispatchBus signal a worker emits to report completion, correlated by id. */\nexport const AGENT_COMPLETED_SIGNAL = 'agent.completed';\n\n/**\n * The **per-worker** signal type an async invocation is emitted on, so a\n * processor only ever claims invocations for the worker class it serves.\n *\n * The async transport emits `agent.invoke.<agentClass>` (the class rendered as a\n * single, provider-safe signal segment) rather than the bare `agent.invoke`.\n * DispatchBus `process()` claims pending rows by *subscribed signal type* before\n * a handler can inspect the payload, so a processor targeting worker A\n * (subscribed to `agent.invoke.<A>`) can never claim a worker-B invocation\n * (`agent.invoke.<B>`). A generic processor that handles every class subscribes\n * to the single-segment wildcard `agent.invoke.*`.\n */\nexport function agentInvokeSignalType(agentClass: string): string {\n const segment = agentClass.replace(/[^A-Za-z0-9_-]/g, '-') || 'unknown';\n return `${AGENT_INVOKE_SIGNAL}.${segment}`;\n}\n\n/**\n * A tool executed under a {@link PrincipalRun} that is not a manifest CRUD\n * operation — e.g. the orchestration invoke-agent tool. It carries its own AI\n * definition and handler, and is offered through the conversational tool loop\n * alongside manifest tools, gated by the same fail-closed `allowedTools`.\n *\n * Defined here (in `@happyvertical/smrt-agents`) rather than in the chat loop so\n * the acyclic `chat → agents` dependency direction is preserved: the loop\n * imports this contract, agents produces implementations of it.\n */\nexport interface PrincipalTool {\n /** Tool name + permission slug, gated by the persona's `allowedTools`. */\n slug: string;\n /** The provider tool definition offered to the model. */\n aiTool: AITool;\n /** Execute the tool under the principal run (should re-assert its own gate). */\n execute(ctx: PrincipalToolContext): Promise<unknown>;\n}\n\n/** Context handed to a {@link PrincipalTool.execute}. */\nexport interface PrincipalToolContext {\n /** The principal run whose context bounds this execution. */\n run: PrincipalRun;\n /** Parsed tool arguments. */\n args: Record<string, unknown>;\n /** The database handle for side-door operations. */\n db?: SmrtClassOptions['db'];\n}\n\n/** A worker invocation handed to a {@link WorkerRunner}. */\nexport interface WorkerInvocation {\n /** The principal run the worker executes within (the delegated principal). */\n run: PrincipalRun;\n /** The delegation envelope (principal, depth, correlation). */\n envelope: DelegationEnvelope;\n /** The target worker agent class. */\n agentClass: string;\n /** The task payload handed to the worker. */\n task: Record<string, unknown>;\n /** The database handle for the worker's operations. */\n db?: SmrtClassOptions['db'];\n}\n\n/**\n * Performs a worker's actual work under the delegated principal. Injected so\n * orchestration stays decoupled from *what* a worker does (run an `Agent`, run a\n * nested persona conversation, call a domain method); the runner receives a\n * {@link PrincipalRun} already bound to the originating user's permissions.\n */\nexport type WorkerRunner = (invocation: WorkerInvocation) => Promise<unknown>;\n\n/**\n * A worker's completion, correlated back to the invocation that produced it.\n */\nexport interface AgentCompletion {\n /** Correlates this completion to the invocation. */\n correlationId: string;\n /** The worker agent class that ran. */\n agentClass: string;\n /** The originating user the worker acted on behalf of. */\n onBehalfOfUserId: string;\n /** Whether the worker's work succeeded. */\n ok: boolean;\n /** The worker's result, when it succeeded. */\n result?: unknown;\n /** The error message, when it failed. */\n error?: string;\n}\n\n/** The outcome the invoke-agent tool returns to the conversation. */\nexport interface InvokeAgentResult {\n /**\n * `completed` / `failed` for an in-process (inline) invocation whose result is\n * surfaced in the same turn; `enqueued` for an async transport whose\n * completion is surfaced later via {@link surfaceAgentCompletions}.\n */\n status: 'completed' | 'failed' | 'enqueued';\n /** Correlates a later completion dispatch back to this invocation. */\n correlationId: string;\n /** The worker agent class invoked. */\n agentClass: string;\n /** The delegation depth of the invoked worker. */\n depth: number;\n /** The worker's result, when it completed in-process. */\n result?: unknown;\n /** The error message, when it failed in-process. */\n error?: string;\n}\n\n/** A delivery handed to an {@link InvokeAgentTransport}. */\nexport interface InvokeAgentDelivery {\n /** The child delegation envelope for the worker. */\n envelope: DelegationEnvelope;\n /** The target worker agent class. */\n agentClass: string;\n /** The task payload for the worker. */\n task: Record<string, unknown>;\n /** The worker runner (used by in-process transports; ignored by async ones). */\n worker: WorkerRunner;\n /** The database handle. */\n db?: SmrtClassOptions['db'];\n /** DispatchBus for the correlated invoke/completion signals. */\n dispatchBus?: DispatchBus;\n /** Audit sink forwarded to {@link executeAsPrincipal}. */\n audit?: PrincipalAuditSink;\n /** Opt into Postgres RLS transaction wrapping. */\n postgresRls?: boolean;\n /** Logger for the default audit sink. */\n logger?: Logger;\n}\n\n/**\n * How a worker invocation is delivered: run it in-process now (inline), emit a\n * DispatchBus signal for a worker to process, or enqueue a job. Swapping the\n * transport never changes the principal-delegation or completion semantics.\n */\nexport interface InvokeAgentTransport {\n deliver(delivery: InvokeAgentDelivery): Promise<InvokeAgentResult>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Whether a value is a structurally-valid {@link DelegationEnvelope} — a\n * concrete principal (`runAsUserId`), a `string | null` tenant, an originating\n * `onBehalfOfUserId`, and an integer `depth`. Used to reject a malformed\n * envelope arriving from a persisted (untrusted) dispatch payload before it\n * drives a worker.\n */\nfunction isValidDelegationEnvelope(\n value: unknown,\n): value is DelegationEnvelope {\n if (!isRecord(value)) {\n return false;\n }\n return (\n typeof value.runAsUserId === 'string' &&\n value.runAsUserId.length > 0 &&\n (value.tenantId === null || typeof value.tenantId === 'string') &&\n typeof value.onBehalfOfUserId === 'string' &&\n value.onBehalfOfUserId.length > 0 &&\n Number.isInteger(value.depth) &&\n typeof value.correlationId === 'string'\n );\n}\n\n/**\n * Run a worker as the delegated principal and report its completion.\n *\n * The worker executes inside a single {@link executeAsPrincipal} context bound\n * to the envelope's principal (`runAsUserId` + `tenantId`) and acting **on\n * behalf of** the originating user — so its authority is the originating user's\n * live RBAC and every action audits back to that user. On completion (success\n * or failure) a correlated `agent.completed` dispatch is emitted **inside** the\n * principal's tenant context, so it is stamped with the right tenant and the\n * orchestrator can surface it back into the conversation.\n */\nexport async function executeDelegatedInvocation(options: {\n envelope: DelegationEnvelope;\n agentClass: string;\n task: Record<string, unknown>;\n worker: WorkerRunner;\n db?: SmrtClassOptions['db'];\n dispatchBus?: DispatchBus;\n audit?: PrincipalAuditSink;\n postgresRls?: boolean;\n logger?: Logger;\n}): Promise<AgentCompletion> {\n const {\n envelope,\n agentClass,\n task,\n worker,\n db,\n dispatchBus,\n audit,\n postgresRls,\n logger,\n } = options;\n\n return executeAsPrincipal(\n {\n db,\n principal: {\n // The principal is the envelope's — the originating user, immutable.\n runAsUserId: envelope.runAsUserId,\n tenantId: envelope.tenantId,\n allowedTools: envelope.allowedTools,\n },\n onBehalfOfUserId: envelope.onBehalfOfUserId,\n agentClass,\n action: 'agent.invoke',\n auditMetadata: {\n correlationId: envelope.correlationId,\n depth: envelope.depth,\n },\n audit,\n postgresRls,\n logger,\n },\n async (run): Promise<AgentCompletion> => {\n let completion: AgentCompletion;\n try {\n const result = await worker({ run, envelope, agentClass, task, db });\n completion = {\n correlationId: envelope.correlationId,\n agentClass,\n onBehalfOfUserId: envelope.onBehalfOfUserId,\n ok: true,\n result,\n };\n } catch (error) {\n completion = {\n correlationId: envelope.correlationId,\n agentClass,\n onBehalfOfUserId: envelope.onBehalfOfUserId,\n ok: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n // Emit the correlated completion inside the tenant context so it is\n // stamped with the principal's tenant (readable by the orchestrator).\n if (dispatchBus) {\n await emitAgentCompletion(dispatchBus, completion);\n }\n return completion;\n },\n );\n}\n\n/**\n * Emit a correlated `agent.completed` dispatch for a worker's completion.\n */\nexport async function emitAgentCompletion(\n dispatchBus: DispatchBus,\n completion: AgentCompletion,\n): Promise<void> {\n await dispatchBus.emit(\n AGENT_COMPLETED_SIGNAL,\n {\n agentClass: completion.agentClass,\n onBehalfOfUserId: completion.onBehalfOfUserId,\n ok: completion.ok,\n result: completion.result,\n error: completion.error,\n },\n {\n correlationId: completion.correlationId,\n source: completion.agentClass || 'agent',\n },\n );\n}\n\n/**\n * Read the correlated completions for an invocation, so the orchestrator can\n * surface a worker's result back into the conversation on a later turn (the\n * async transport). Returns `[]` when nothing has completed yet.\n */\nexport async function surfaceAgentCompletions(\n dispatchBus: DispatchBus,\n correlationId: string,\n): Promise<AgentCompletion[]> {\n const dispatches = await dispatchBus.list({\n type: AGENT_COMPLETED_SIGNAL,\n correlationId,\n });\n return dispatches.map((dispatch) => {\n const payload = dispatch.payload as Record<string, unknown>;\n return {\n correlationId,\n agentClass:\n typeof payload.agentClass === 'string' ? payload.agentClass : '',\n onBehalfOfUserId:\n typeof payload.onBehalfOfUserId === 'string'\n ? payload.onBehalfOfUserId\n : '',\n ok: payload.ok === true,\n result: payload.result,\n error: typeof payload.error === 'string' ? payload.error : undefined,\n };\n });\n}\n\n/**\n * The default transport: run the worker in-process now and return its completion\n * as the tool observation, so the result is surfaced back into the conversation\n * in the same turn.\n */\nexport const inlineInvokeAgentTransport: InvokeAgentTransport = {\n async deliver(delivery): Promise<InvokeAgentResult> {\n const completion = await executeDelegatedInvocation({\n envelope: delivery.envelope,\n agentClass: delivery.agentClass,\n task: delivery.task,\n worker: delivery.worker,\n db: delivery.db,\n dispatchBus: delivery.dispatchBus,\n audit: delivery.audit,\n postgresRls: delivery.postgresRls,\n logger: delivery.logger,\n });\n return {\n status: completion.ok ? 'completed' : 'failed',\n correlationId: completion.correlationId,\n agentClass: completion.agentClass,\n depth: delivery.envelope.depth,\n result: completion.result,\n error: completion.error,\n };\n },\n};\n\n/**\n * An async transport that emits a correlated, **per-worker** `agent.invoke.<class>`\n * DispatchBus signal for a worker to process out of band\n * ({@link processAgentInvocations}). The tool returns `enqueued`; the worker's\n * completion is surfaced later via {@link surfaceAgentCompletions}. The worker\n * runner is *not* used here — it is reconstructed on the processing side.\n *\n * Emitting on the per-worker signal type (see {@link agentInvokeSignalType})\n * means a processor for worker A never claims an invocation targeted at worker\n * B, even under compete delivery.\n */\nexport function createDispatchInvokeTransport(\n dispatchBus: DispatchBus,\n options: { source?: string } = {},\n): InvokeAgentTransport {\n return {\n async deliver(delivery): Promise<InvokeAgentResult> {\n await dispatchBus.emit(\n agentInvokeSignalType(delivery.agentClass),\n {\n envelope: delivery.envelope,\n agentClass: delivery.agentClass,\n task: delivery.task,\n },\n {\n correlationId: delivery.envelope.correlationId,\n source: options.source ?? 'agents.orchestrator',\n },\n );\n return {\n status: 'enqueued',\n correlationId: delivery.envelope.correlationId,\n agentClass: delivery.agentClass,\n depth: delivery.envelope.depth,\n };\n },\n };\n}\n\n/**\n * Process pending `agent.invoke` signals, running each worker as its delegated\n * principal and emitting the correlated completion. This is the worker side of\n * {@link createDispatchInvokeTransport}.\n *\n * Pass `agentClass` to target a single worker class — the processor subscribes\n * to `agent.invoke.<class>` and can only ever claim that class's invocations, so\n * running one processor per worker class never cross-claims. Omit it for a\n * generic processor that handles every class (subscribes to the wildcard\n * `agent.invoke.*` and dispatches on the payload's `agentClass`).\n *\n * The envelope arrives from a (persisted, thus untrusted) dispatch payload, so\n * it is validated ({@link isValidDelegationEnvelope}) and its depth re-asserted\n * before the worker runs — a malformed or tampered envelope cannot drive the\n * chain past {@link MAX_DELEGATION_DEPTH}.\n *\n * @returns The number of invocations processed.\n */\nexport async function processAgentInvocations(options: {\n dispatchBus: DispatchBus;\n subscriber: string;\n worker: WorkerRunner;\n /** Target a single worker class; omit for a handle-every-class processor. */\n agentClass?: string;\n db?: SmrtClassOptions['db'];\n audit?: PrincipalAuditSink;\n postgresRls?: boolean;\n logger?: Logger;\n limit?: number;\n}): Promise<number> {\n const { dispatchBus, subscriber, worker, db, audit, postgresRls, logger } =\n options;\n const log = logger ?? createLogger({ level: 'info' });\n // Targeted processors subscribe to their own class's signal; a generic\n // processor uses the single-segment wildcard to handle every class.\n const signalType = options.agentClass\n ? agentInvokeSignalType(options.agentClass)\n : `${AGENT_INVOKE_SIGNAL}.*`;\n await dispatchBus.subscribe({ signalType, subscriber });\n return dispatchBus.process(\n subscriber,\n async (payload) => {\n const record = isRecord(payload) ? payload : {};\n const envelope = record.envelope;\n const agentClass =\n typeof record.agentClass === 'string' ? record.agentClass : '';\n // Reject a malformed/tampered payload before it drives a worker.\n if (!isValidDelegationEnvelope(envelope) || !agentClass) {\n log.warn(\n 'agent.invoke dispatch has an invalid envelope or agentClass',\n {\n agentClass,\n },\n );\n return;\n }\n // Defense in depth: a persisted (tamperable) envelope cannot exceed the\n // depth ceiling (or carry a NaN/negative depth that bypasses it).\n assertWithinDelegationDepth(envelope.depth);\n await executeDelegatedInvocation({\n envelope,\n agentClass,\n task: isRecord(record.task) ? record.task : {},\n worker,\n db,\n dispatchBus,\n audit,\n postgresRls,\n logger,\n });\n },\n { limit: options.limit },\n );\n}\n\n/**\n * Options for {@link createInvokeAgentTool}.\n */\nexport interface CreateInvokeAgentToolOptions {\n /**\n * The **current run's** delegation envelope — the orchestrator's own (depth\n * `0`) when building the tool for a conversation, or a worker's own envelope\n * when building it for a nested/further delegation. Its principal is the\n * ceiling every child inherits; the live run context is the source of truth\n * for the principal and overrides this copy.\n */\n parentEnvelope: DelegationEnvelope;\n /** The worker runner used by in-process transports. */\n worker: WorkerRunner;\n /** Database handle for the worker's operations. */\n db?: SmrtClassOptions['db'];\n /** DispatchBus for correlated invoke/completion signals. */\n dispatchBus?: DispatchBus;\n /** Delivery transport. Defaults to {@link inlineInvokeAgentTransport}. */\n transport?: InvokeAgentTransport;\n /** Audit sink forwarded to {@link executeAsPrincipal}. */\n audit?: PrincipalAuditSink;\n /** Opt into Postgres RLS transaction wrapping. */\n postgresRls?: boolean;\n /** Logger for the default audit sink. */\n logger?: Logger;\n /** Resolve a worker's tool ceiling from its class (e.g. its persona tools). */\n resolveWorkerAllowedTools?: (agentClass: string) => string[] | undefined;\n /** Depth ceiling override (mainly for tests). */\n maxDepth?: number;\n /** Override the tool description offered to the model. */\n description?: string;\n}\n\n/**\n * Build the standard **invoke-agent** tool.\n *\n * Offered through the conversational tool loop and gated by the persona's\n * `allowedTools` (the model may only call it when `agents.invoke` is\n * allow-listed). Its handler:\n *\n * 1. re-asserts the fail-closed allow-list ({@link PrincipalRun.assertToolAllowed});\n * 2. derives the child {@link DelegationEnvelope} with the principal taken\n * **from the live run context** — never from the tool arguments — so a worker\n * cannot widen the principal, and increments the bounded depth;\n * 3. delivers the invocation via the configured transport.\n *\n * The child inherits the orchestrator's principal verbatim and acts on behalf of\n * the same originating user, so the worker runs under the originating user's\n * permissions and audits back to them.\n */\nexport function createInvokeAgentTool(\n options: CreateInvokeAgentToolOptions,\n): PrincipalTool {\n const transport = options.transport ?? inlineInvokeAgentTransport;\n return {\n slug: INVOKE_AGENT_TOOL_SLUG,\n aiTool: {\n type: 'function',\n function: {\n name: INVOKE_AGENT_FUNCTION_NAME,\n description:\n options.description ??\n 'Delegate a task to a worker agent. The worker runs under YOUR ' +\n 'principal (the originating user) — it cannot exceed your ' +\n 'permissions — and returns its completion.',\n // Note: the tool deliberately exposes NO `allowedTools` / principal\n // parameters — a worker's tool ceiling and principal are never taken\n // from model-controlled arguments (see below).\n parameters: {\n type: 'object',\n required: ['agentClass'],\n properties: {\n agentClass: {\n type: 'string',\n description: 'The worker agent class to invoke.',\n },\n task: {\n type: 'object',\n description: 'The task payload handed to the worker.',\n },\n },\n },\n },\n },\n async execute({ run, args }): Promise<InvokeAgentResult> {\n // Execution gate (defense-in-depth behind the offer gate): the persona\n // must allow-list agents.invoke.\n run.assertToolAllowed(INVOKE_AGENT_TOOL_SLUG);\n\n const agentClass =\n typeof args.agentClass === 'string' ? args.agentClass.trim() : '';\n if (!agentClass) {\n throw new Error(\"invoke-agent requires a non-empty 'agentClass'.\");\n }\n const task = isRecord(args.task) ? args.task : {};\n\n // The principal is taken from the LIVE run context, never from the tool\n // arguments — this is what makes the principal immutable along the chain:\n // a worker calling invoke-agent cannot pass a broader `runAsUserId` /\n // `tenantId`, because they are not read from `args` at all.\n const parent: DelegationEnvelope = {\n ...options.parentEnvelope,\n runAsUserId: run.context.userId ?? options.parentEnvelope.runAsUserId,\n tenantId: run.context.tenantId ?? options.parentEnvelope.tenantId,\n };\n\n // The worker's tool ceiling comes ONLY from trusted server-side policy\n // (`resolveWorkerAllowedTools`), never from the model-controlled tool\n // arguments — otherwise the model could hand the worker an arbitrary tool\n // set. Absent a resolver the worker gets NO tools (fail-closed); its\n // authority is still bounded by the originating user's RBAC regardless.\n const requestedAllowedTools =\n options.resolveWorkerAllowedTools?.(agentClass);\n\n const childEnvelope = deriveDelegationEnvelope(parent, {\n allowedTools: requestedAllowedTools,\n maxDepth: options.maxDepth,\n });\n\n return transport.deliver({\n envelope: childEnvelope,\n agentClass,\n task,\n worker: options.worker,\n db: options.db,\n dispatchBus: options.dispatchBus,\n audit: options.audit,\n postgresRls: options.postgresRls,\n logger: options.logger,\n });\n },\n };\n}\n","import {\n field,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport {\n queryGlobal,\n queryWithGlobals,\n TenantScoped,\n tenantId,\n} from '@happyvertical/smrt-tenancy';\nimport {\n getAgentClassName,\n getAgentTypeAliases,\n getAgentTypeName,\n} from './identity.js';\n\n/**\n * Status of a scheduled agent\n */\nexport type ScheduleStatus = 'active' | 'paused' | 'disabled' | 'error';\n\n/**\n * AgentSchedule model for cron-based agent scheduling\n *\n * This extends SmrtObject to store schedule metadata in the SMRT database.\n * Schedules are processed by the TaskRunner which creates jobs at scheduled times.\n *\n * @example\n * ```typescript\n * const schedule = new AgentSchedule({\n * agentType: 'Praeco',\n * agentId: 'praeco-main',\n * cron: '0 2 * * *', // Run at 2 AM daily\n * enabled: true,\n * });\n * await schedule.initialize();\n * await schedule.save();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: '_smrt_agent_schedules',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: {\n include: ['list', 'get', 'create', 'update', 'delete', 'enable', 'disable'],\n // enable/disable are operator commands invoked in-process via the CLI;\n // they intentionally aren't exposed over HTTP.\n skipApiCheck: true,\n },\n mcp: { include: ['list', 'get'] },\n // ScheduleRunner.poll() scans this predicate every minute (#2364, epic\n // #2382 finding A3): `enabled = true AND status = 'active' AND\n // next_run <= ?`, ordered by `next_run ASC`. `enabled` and `status` are\n // both equality filters and lead `next_run`, the range filter: a B-tree\n // composite serves an equality prefix as a direct lookup but can only\n // range-scan its trailing column, so the two equality columns come first\n // (the composite still serves `status`-only and `(enabled, status)`-only\n // reads as leftward prefixes).\n indexes: [\n {\n name: '_smrt_agent_schedules_enabled_status_next_run_idx',\n columns: ['enabled', 'status', 'nextRun'],\n },\n ],\n})\nexport class AgentSchedule extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global schedules\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Canonical agent type to run (qualified name when available) */\n @field({ type: 'text' })\n agentType: string = '';\n\n /** Optional agent instance ID (for running specific instances) */\n @field({ type: 'text', nullable: true })\n agentId: string | null = null;\n\n /**\n * Agent configuration to pass when running.\n *\n * Sensitive (#1540): may carry API keys/credentials, so it is excluded from\n * generated API/MCP responses and rejected as a `where` filter key.\n */\n @field({ type: 'json', sqlType: 'TEXT', sensitive: true })\n agentConfig: Record<string, unknown> = {};\n\n /** Cron expression (e.g., '0 2 * * *' for 2 AM daily) */\n @field({ type: 'text' })\n cron: string = '';\n\n /** Timezone for cron interpretation (default: UTC) */\n @field({ type: 'text' })\n timezone: string = 'UTC';\n\n /** Whether the schedule is enabled */\n @field({ type: 'boolean' })\n enabled: boolean = true;\n\n /** Current schedule status */\n @field({ type: 'text' })\n status: ScheduleStatus = 'active';\n\n /** Last time the agent was run */\n @field({ type: 'datetime', nullable: true })\n lastRun: Date | null = null;\n\n /** Next scheduled run time */\n @field({ type: 'datetime', nullable: true })\n nextRun: Date | null = null;\n\n /** Status of the last run */\n @field({ type: 'text', nullable: true })\n lastStatus: 'success' | 'failed' | null = null;\n\n /** Error message from last failed run */\n @field({ type: 'text', nullable: true })\n lastError: string | null = null;\n\n /** Total number of runs */\n @field({ type: 'integer' })\n runCount: number = 0;\n\n /** Total number of successful runs */\n @field({ type: 'integer' })\n successCount: number = 0;\n\n /** Total number of failed runs */\n @field({ type: 'integer' })\n failureCount: number = 0;\n\n /** Maximum concurrent runs (prevent overlapping) */\n @field({ type: 'integer' })\n maxConcurrent: number = 1;\n\n /** Current number of running instances */\n @field({ type: 'integer' })\n runningCount: number = 0;\n\n /** Timeout for agent execution in milliseconds (default: 1 hour) */\n @field({ type: 'integer' })\n timeout: number = 3600000;\n\n /** Method to call on the agent (default: 'run') */\n @field({ type: 'text' })\n method: string = 'run';\n\n /** Arguments to pass to the method */\n @field({ type: 'json', sqlType: 'TEXT' })\n methodArgs: Record<string, unknown> = {};\n\n /**\n * Enable the schedule\n */\n async enable(): Promise<void> {\n this.enabled = true;\n this.status = 'active';\n this.calculateNextRun();\n await this.save();\n }\n\n /**\n * Disable the schedule\n */\n async disable(): Promise<void> {\n this.enabled = false;\n this.status = 'disabled';\n await this.save();\n }\n\n /**\n * Pause the schedule temporarily\n */\n async pause(): Promise<void> {\n this.status = 'paused';\n await this.save();\n }\n\n /**\n * Resume a paused schedule\n */\n async resume(): Promise<void> {\n if (this.enabled) {\n this.status = 'active';\n this.calculateNextRun();\n }\n await this.save();\n }\n\n /**\n * Calculate the next run time based on cron expression\n */\n calculateNextRun(): void {\n if (!this.cron || !this.enabled) {\n this.nextRun = null;\n return;\n }\n\n try {\n const next = getNextCronDate(this.cron, this.timezone);\n this.nextRun = next;\n } catch {\n this.nextRun = null;\n this.status = 'error';\n this.lastError = `Invalid cron expression: ${this.cron}`;\n }\n }\n\n /**\n * Get a human-readable description of the schedule\n */\n getDescription(): string {\n const displayAgentType = getAgentClassName(this.agentType);\n const agent = this.agentId\n ? `${displayAgentType}#${this.agentId}`\n : displayAgentType;\n return `${agent}.${this.method}() @ ${this.cron}`;\n }\n\n /**\n * Lifecycle hook - calculate next run on save\n */\n async beforeSave(): Promise<void> {\n if (this.agentType) {\n this.agentType = getAgentTypeName(this.agentType);\n }\n if (!this.nextRun && this.enabled) {\n this.calculateNextRun();\n }\n }\n}\n\n/**\n * Collection for managing AgentSchedule objects\n */\nexport class AgentScheduleCollection extends SmrtCollection<AgentSchedule> {\n static readonly _itemClass = AgentSchedule;\n\n /**\n * Find all schedules for a specific tenant\n * @param tenantId - Tenant ID to filter by\n * @returns Array of AgentSchedule objects for the tenant\n */\n async findByTenant(tenantId: string): Promise<AgentSchedule[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global schedules (not associated with any tenant).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global AgentSchedule objects\n */\n async findGlobal(): Promise<AgentSchedule[]> {\n return queryGlobal<AgentSchedule>(this);\n }\n\n /**\n * Find schedules for a tenant including global schedules.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - Tenant ID to include\n * @returns Array of AgentSchedule objects for the tenant and global schedules\n */\n async findWithGlobals(tenantId: string): Promise<AgentSchedule[]> {\n return queryWithGlobals<AgentSchedule>(\n this,\n tenantId,\n 'AgentSchedule.findWithGlobals',\n );\n }\n\n /**\n * List schedules by status\n */\n async listByStatus(\n status: ScheduleStatus | ScheduleStatus[],\n options: { limit?: number } = {},\n ): Promise<AgentSchedule[]> {\n return this.list({\n where: {\n status: Array.isArray(status) ? status : [status],\n },\n orderBy: 'next_run ASC',\n limit: options.limit,\n });\n }\n\n /**\n * List schedules for a specific agent type\n */\n async listByAgentType(\n agentType: string,\n options: { limit?: number; includeDisabled?: boolean } = {},\n ): Promise<AgentSchedule[]> {\n const aliases = getAgentTypeAliases(agentType);\n const where: Record<string, unknown> =\n aliases.length > 1\n ? { 'agentType in': aliases }\n : { agentType: getAgentTypeName(agentType) };\n if (!options.includeDisabled) {\n where.enabled = true;\n }\n\n return this.list({\n where,\n orderBy: 'next_run ASC',\n limit: options.limit,\n });\n }\n}\n\n/**\n * Parse a cron expression and get the next run date.\n *\n * Supports standard 5-field cron format: minute hour day-of-month month\n * day-of-week. Day-of-month / day-of-week follow POSIX OR semantics when both\n * are restricted (see the loop body). Matched against the host's local time\n * (not timezone-aware).\n *\n * Examples:\n * - '0 2 * * *' - 2:00 AM daily\n * - '0 0 * * 0' - Midnight on Sundays\n * - 'x/15 * * * *' - Every 15 minutes (where x is asterisk)\n * - '0 9 1 * *' - 9:00 AM on the 1st of every month\n *\n * Exported for unit testing of the matching logic.\n */\nexport function getNextCronDate(cron: string, _timezone: string = 'UTC'): Date {\n const parts = cron.trim().split(/\\s+/);\n if (parts.length !== 5) {\n throw new Error(\n `Invalid cron expression: expected 5 fields, got ${parts.length}`,\n );\n }\n\n const [minuteExpr, hourExpr, dayExpr, monthExpr, dowExpr] = parts;\n\n const now = new Date();\n const candidate = new Date(now);\n candidate.setSeconds(0);\n candidate.setMilliseconds(0);\n\n // Move to next minute at minimum\n candidate.setMinutes(candidate.getMinutes() + 1);\n\n // Standard cron DOM/DOW semantics:\n // When both day-of-month and day-of-week are restricted (not *),\n // a date matches if EITHER condition is met (OR logic). When only one\n // is restricted, only that field applies; when both are `*`, every day\n // matches. POSIX: `0 0 13 * 5` fires on the 13th OR any Friday.\n const dayIsWildcard = dayExpr === '*';\n const dowIsWildcard = dowExpr === '*';\n\n // Search for next matching date (limit to 1 year)\n const maxIterations = 525600; // ~1 year in minutes\n for (let i = 0; i < maxIterations; i++) {\n const dayMatches = matchesCronField(candidate.getDate(), dayExpr);\n // getDay() returns 0 for Sunday; standard cron accepts both 0 and 7\n const dow = candidate.getDay();\n const dowMatches =\n matchesCronField(dow, dowExpr) ||\n (dow === 0 && matchesCronField(7, dowExpr));\n\n let dayOfMonthOrWeekMatches: boolean;\n if (!dayIsWildcard && !dowIsWildcard) {\n dayOfMonthOrWeekMatches = dayMatches || dowMatches;\n } else if (!dayIsWildcard) {\n dayOfMonthOrWeekMatches = dayMatches;\n } else if (!dowIsWildcard) {\n dayOfMonthOrWeekMatches = dowMatches;\n } else {\n dayOfMonthOrWeekMatches = true;\n }\n\n if (\n matchesCronField(candidate.getMonth() + 1, monthExpr) &&\n dayOfMonthOrWeekMatches &&\n matchesCronField(candidate.getHours(), hourExpr) &&\n matchesCronField(candidate.getMinutes(), minuteExpr)\n ) {\n return candidate;\n }\n\n candidate.setMinutes(candidate.getMinutes() + 1);\n }\n\n throw new Error(`Could not find next run date for cron: ${cron}`);\n}\n\n/**\n * Check if a value matches a cron field expression\n */\nfunction matchesCronField(value: number, expr: string): boolean {\n // Wildcard matches everything\n if (expr === '*') {\n return true;\n }\n\n // Handle step values (*/5, 0-30/2)\n if (expr.includes('/')) {\n const [range, stepStr] = expr.split('/');\n const step = parseInt(stepStr, 10);\n if (range === '*') {\n return value % step === 0;\n }\n // Handle range with step\n if (range.includes('-')) {\n const [startStr, endStr] = range.split('-');\n const start = parseInt(startStr, 10);\n const end = parseInt(endStr, 10);\n if (value < start || value > end) return false;\n return (value - start) % step === 0;\n }\n }\n\n // Handle ranges (1-5)\n if (expr.includes('-')) {\n const [startStr, endStr] = expr.split('-');\n const start = parseInt(startStr, 10);\n const end = parseInt(endStr, 10);\n return value >= start && value <= end;\n }\n\n // Handle lists (1,3,5)\n if (expr.includes(',')) {\n const values = expr.split(',').map((v) => parseInt(v.trim(), 10));\n return values.includes(value);\n }\n\n // Exact match\n return value === parseInt(expr, 10);\n}\n\nexport default AgentSchedule;\n","/**\n * TenantAgent - Junction between tenants and agents\n *\n * Represents the binding of an agent class to a specific tenant,\n * with optional permission overrides and status control.\n *\n * The absence of a row means \"check parent tenant\" — inheritance\n * is a resolution behavior, not stored state.\n */\n\nimport {\n field,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n getAgentClassName,\n getAgentTypeAliases,\n getAgentTypeName,\n} from './identity.js';\nimport type { AgentManifestInfo } from './ui.js';\n\n/**\n * Status of a tenant-agent binding\n */\nexport type TenantAgentStatus = 'active' | 'disabled';\n\n/**\n * Permission definition for merge logic\n */\ninterface PermissionDef {\n id: string;\n defaultGranted?: boolean;\n}\n\n/**\n * Result of resolving agent availability for a tenant\n */\nexport interface ResolvedAgentAvailability {\n /** Human-readable agent class name (e.g., 'Praeco') */\n agentClass: string;\n /** Canonical agent type (qualified name when available) */\n agentType: string;\n /** Resolved status */\n status: TenantAgentStatus;\n /** How this was resolved */\n source: 'explicit' | 'inherited';\n /** Which tenant the binding came from */\n sourceTenantId: string;\n /** Merged permissions (manifest defaults overridden by explicit grants/revokes) */\n permissions: Record<string, boolean>;\n /** The agent instance ID (row in agents table), if one exists */\n agentId?: string;\n /** Agent manifest from the build (if available) */\n manifest?: AgentManifestInfo;\n /** Tenant-level config overrides */\n config?: Record<string, unknown>;\n}\n\n/**\n * TenantAgent SmrtObject — junction between tenants and agents\n *\n * Each row represents an explicit binding of an agent class to a tenant.\n * - Presence means explicit override (active or disabled)\n * - Absence means \"check parent tenant\" (inheritance)\n *\n * Permission overrides:\n * - null/missing key → use defaultGranted from manifest\n * - true → explicitly granted\n * - false → explicitly revoked\n */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'tenant_agents',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n conflictColumns: ['tenant_id', 'agent_class'],\n})\nexport class TenantAgent extends SmrtObject {\n @tenantId()\n tenantId: string = '';\n\n /** Canonical agent type (qualified name when available) */\n @field({ type: 'text' })\n agentClass: string = '';\n\n /** Status of the agent for this tenant */\n @field({ type: 'text' })\n status: TenantAgentStatus = 'active';\n\n /** Explicit permission overrides (JSON). null = use manifest defaults */\n @field({ type: 'json', nullable: true })\n permissions: Record<string, boolean> | null = null;\n\n /**\n * Tenant-level agent config overrides (JSON).\n *\n * Sensitive (S5 #1398): like {@link AgentConfig.configData} and\n * {@link AgentSchedule.agentConfig} (both marked sensitive in #1540), these\n * per-tenant override blobs routinely carry API keys/credentials. Exclude\n * them from generated API/MCP responses and reject them as a `where` filter\n * key. Server-side helpers (e.g. `serializeResolvedAgent`) still read the\n * property directly, so the admin dashboard flow is unaffected.\n */\n @field({ type: 'json', nullable: true, sensitive: true })\n config: Record<string, unknown> | null = null;\n}\n\n/**\n * Collection for managing tenant-agent bindings\n */\nexport class TenantAgentCollection extends SmrtCollection<TenantAgent> {\n static readonly _itemClass = TenantAgent;\n\n /**\n * Resolve agent availability for a tenant, walking up the hierarchy.\n *\n * Algorithm:\n * 1. Load explicit entries for this tenant\n * 2. Build result map from explicit entries (source = 'explicit')\n * 3. Merge permissions: manifest defaults overridden by explicit permissions\n * 4. Get tenant's ancestors via hierarchyPath (immediate parent → root)\n * 5. For each ancestor, add inherited agents not already resolved\n * 6. Return only agents that appear somewhere in the hierarchy\n *\n * @param tenantId - The tenant to resolve for\n * @param getAncestorIds - Function that returns ancestor tenant IDs (parent → root order)\n * @param manifests - Map of agent class name to AgentManifestInfo\n */\n async resolveForTenant(\n tenantId: string,\n getAncestorIds: (tenantId: string) => Promise<string[]>,\n manifests?: Map<string, AgentManifestInfo>,\n ): Promise<ResolvedAgentAvailability[]> {\n const result = new Map<string, ResolvedAgentAvailability>();\n\n // Step 1: Load explicit entries for this tenant\n const explicitEntries = await this.list({\n where: { tenantId },\n });\n\n // Step 2: Build result from explicit entries\n for (const entry of explicitEntries) {\n const agentType = await this.normalizeStoredAgentClass(entry);\n const manifest = getManifestForAgent(manifests, agentType);\n const mergedPermissions = mergePermissions(\n manifest?.permissions,\n entry.permissions,\n );\n\n result.set(agentType, {\n agentClass: getAgentClassName(agentType),\n agentType,\n status: entry.status,\n source: 'explicit',\n sourceTenantId: tenantId,\n permissions: mergedPermissions,\n manifest,\n config: entry.config ?? undefined,\n });\n }\n\n // Step 3: Walk ancestors for inherited agents\n const ancestorIds = await getAncestorIds(tenantId);\n for (const ancestorId of ancestorIds) {\n const ancestorEntries = await this.list({\n where: { tenantId: ancestorId },\n });\n\n for (const entry of ancestorEntries) {\n const agentType = await this.normalizeStoredAgentClass(entry);\n // Skip if already resolved explicitly or from a closer ancestor\n if (result.has(agentType)) continue;\n\n const manifest = getManifestForAgent(manifests, agentType);\n const mergedPermissions = mergePermissions(\n manifest?.permissions,\n entry.permissions,\n );\n\n result.set(agentType, {\n agentClass: getAgentClassName(agentType),\n agentType,\n status: entry.status,\n source: 'inherited',\n sourceTenantId: ancestorId,\n permissions: mergedPermissions,\n manifest,\n config: entry.config ?? undefined,\n });\n }\n }\n\n return Array.from(result.values());\n }\n\n /**\n * Enable an agent for a tenant (creates or updates binding)\n */\n async enableAgent(\n tenantId: string,\n agentClass: string,\n ): Promise<TenantAgent> {\n const canonicalAgentClass = getAgentTypeName(agentClass);\n const existing = await this.findByTenantAndClass(tenantId, agentClass);\n if (existing) {\n existing.status = 'active';\n await existing.save();\n return existing;\n }\n\n const entry = await this.create({\n tenantId,\n agentClass: canonicalAgentClass,\n status: 'active',\n });\n await entry.save();\n return entry;\n }\n\n /**\n * Disable an agent for a tenant\n */\n async disableAgent(\n tenantId: string,\n agentClass: string,\n ): Promise<TenantAgent> {\n const canonicalAgentClass = getAgentTypeName(agentClass);\n const existing = await this.findByTenantAndClass(tenantId, agentClass);\n if (existing) {\n existing.status = 'disabled';\n await existing.save();\n return existing;\n }\n\n const entry = await this.create({\n tenantId,\n agentClass: canonicalAgentClass,\n status: 'disabled',\n });\n await entry.save();\n return entry;\n }\n\n /**\n * Remove explicit override, falling back to inheritance\n */\n async clearOverride(tenantId: string, agentClass: string): Promise<void> {\n const existing = await this.findByTenantAndClass(tenantId, agentClass);\n if (existing) {\n await existing.delete();\n }\n }\n\n /**\n * Set permission overrides for a tenant's agent binding\n */\n async setPermissions(\n tenantId: string,\n agentClass: string,\n permissions: Record<string, boolean>,\n ): Promise<TenantAgent> {\n const canonicalAgentClass = getAgentTypeName(agentClass);\n const existing = await this.findByTenantAndClass(tenantId, agentClass);\n if (existing) {\n existing.permissions = permissions;\n await existing.save();\n return existing;\n }\n\n const entry = await this.create({\n tenantId,\n agentClass: canonicalAgentClass,\n status: 'active',\n permissions,\n });\n await entry.save();\n return entry;\n }\n\n /**\n * Find a tenant-agent binding by tenant and agent class\n */\n async findByTenantAndClass(\n tenantId: string,\n agentClass: string,\n ): Promise<TenantAgent | null> {\n const aliases = getAgentTypeAliases(agentClass);\n const results = await this.list({\n where:\n aliases.length > 1\n ? { tenantId, 'agentClass in': aliases }\n : { tenantId, agentClass: aliases[0] },\n });\n\n const canonicalAgentClass = getAgentTypeName(agentClass);\n const found =\n results.find((entry) => entry.agentClass === canonicalAgentClass) ||\n results[0] ||\n null;\n\n if (found && found.agentClass !== canonicalAgentClass) {\n await this.persistCanonicalAgentClass(found, canonicalAgentClass);\n }\n\n return found;\n }\n\n private async normalizeStoredAgentClass(entry: TenantAgent): Promise<string> {\n const canonicalAgentClass = getAgentTypeName(entry.agentClass);\n if (entry.agentClass !== canonicalAgentClass) {\n await this.persistCanonicalAgentClass(entry, canonicalAgentClass);\n }\n return canonicalAgentClass;\n }\n\n private async persistCanonicalAgentClass(\n entry: TenantAgent,\n canonicalAgentClass: string,\n ): Promise<void> {\n if (!entry.id || entry.agentClass === canonicalAgentClass) {\n entry.agentClass = canonicalAgentClass;\n return;\n }\n\n await this._db.query(\n `UPDATE ${this.tableName}\n SET agent_class = ?,\n updated_at = ?\n WHERE id = ?`,\n canonicalAgentClass,\n new Date().toISOString(),\n entry.id,\n );\n\n entry.agentClass = canonicalAgentClass;\n }\n}\n\n/**\n * Merge manifest permission defaults with explicit overrides\n */\nfunction mergePermissions(\n manifestPermissions?: PermissionDef[],\n overrides?: Record<string, boolean> | null,\n): Record<string, boolean> {\n const result: Record<string, boolean> = {};\n\n // Start with manifest defaults\n if (manifestPermissions) {\n for (const perm of manifestPermissions) {\n result[perm.id] = perm.defaultGranted !== false;\n }\n }\n\n // Apply overrides\n if (overrides) {\n for (const [key, value] of Object.entries(overrides)) {\n result[key] = value;\n }\n }\n\n return result;\n}\n\nfunction getManifestForAgent(\n manifests: Map<string, AgentManifestInfo> | undefined,\n agentTypeOrIdentifier: string,\n): AgentManifestInfo | undefined {\n if (!manifests) {\n return undefined;\n }\n\n return (\n manifests.get(agentTypeOrIdentifier) ||\n manifests.get(getAgentClassName(agentTypeOrIdentifier))\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["items","result","tenantId","tenantId"],"sources":["../src/__smrt-register__.ts","../src/ai-config.ts","../src/interests.ts","../src/learning.ts","../src/agent.ts","../src/data-surface.ts","../src/delegation.ts","../src/invoke-agent.ts","../src/report-data-surface.ts","../src/schedule.ts","../src/tenant-agent.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// During library builds, smrtPlugin replaces this entire module with generated\n// code that embeds the scanned manifest inline (#1506/#1507) — published dists\n// never resolve this URL, so downstream bundlers cannot break registration by\n// relocating the compiled module away from dist/manifest.json. The runtime\n// lookup below is the fallback for source-mode runs without that transform.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","import type { AIClientOptions } from '@happyvertical/ai';\nimport { SecretService } from '@happyvertical/smrt-secrets';\nimport { getCurrentTenant, withTenant } from '@happyvertical/smrt-tenancy';\nimport { TenantCollection } from '@happyvertical/smrt-users';\nimport type { DatabaseInterface } from '@happyvertical/sql';\n\nexport type AgentAISecretFallback = 'none' | 'ancestors';\n\nexport interface AgentAIOptions extends AIClientOptions {\n /**\n * Secret name to resolve for the provider API key.\n *\n * When omitted, the agent runtime falls back to a provider-specific default\n * for known providers such as Gemini, OpenAI, and Anthropic.\n */\n apiKeySecretName?: string;\n\n /**\n * Whether to fall back to ancestor tenants when the current tenant does not\n * define the requested secret.\n *\n * Defaults to `'ancestors'`.\n */\n apiKeySecretFallback?: AgentAISecretFallback;\n}\n\ninterface ResolveAgentAIOptionsInput {\n aiConfig: AgentAIOptions | undefined;\n db: DatabaseInterface | null | undefined;\n tenantId?: string | null;\n}\n\nconst DEFAULT_SECRET_NAMES: Record<string, string> = {\n anthropic: 'ANTHROPIC_API_KEY',\n gemini: 'GEMINI_API_KEY',\n openai: 'OPENAI_API_KEY',\n};\n\nconst DEFAULT_SECRET_FALLBACK: AgentAISecretFallback = 'ancestors';\n\nconst secretServiceCache = new WeakMap<\n DatabaseInterface,\n Promise<SecretService>\n>();\nconst tenantCollectionCache = new WeakMap<\n DatabaseInterface,\n Promise<TenantCollection>\n>();\n\nfunction asNonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim().length > 0\n ? value.trim()\n : undefined;\n}\n\nfunction normalizeSecretFallback(value: unknown): AgentAISecretFallback {\n return value === 'none' ? 'none' : DEFAULT_SECRET_FALLBACK;\n}\n\nfunction getDefaultSecretName(aiConfig: AgentAIOptions): string | undefined {\n const provider = asNonEmptyString(aiConfig.type)?.toLowerCase();\n if (!provider) {\n return undefined;\n }\n\n return DEFAULT_SECRET_NAMES[provider];\n}\n\nfunction stripAgentAISecretFields(\n aiConfig: AgentAIOptions,\n): AIClientOptions & Record<string, unknown> {\n const {\n apiKeySecretName: _apiKeySecretName,\n apiKeySecretFallback: _apiKeySecretFallback,\n ...rest\n } = aiConfig;\n return rest;\n}\n\nasync function getSecretService(db: DatabaseInterface): Promise<SecretService> {\n const existing = secretServiceCache.get(db);\n if (existing) {\n return await existing;\n }\n\n const created = SecretService.create({ db });\n secretServiceCache.set(db, created);\n return await created;\n}\n\nasync function getTenantCollection(\n db: DatabaseInterface,\n): Promise<TenantCollection> {\n const existing = tenantCollectionCache.get(db);\n if (existing) {\n return await existing;\n }\n\n const created = TenantCollection.create({ db });\n tenantCollectionCache.set(db, created);\n return await created;\n}\n\nasync function getTenantSearchOrder(\n db: DatabaseInterface,\n tenantId: string,\n fallback: AgentAISecretFallback,\n): Promise<string[]> {\n const tenantIds = [tenantId];\n if (fallback !== 'ancestors') {\n return tenantIds;\n }\n\n const tenants = await getTenantCollection(db);\n const ancestors = await tenants.getAncestors(tenantId);\n for (const tenant of ancestors) {\n if (tenant.id) {\n tenantIds.push(tenant.id);\n }\n }\n\n return tenantIds;\n}\n\nasync function resolveSecretValue(\n service: SecretService,\n tenantIds: string[],\n secretName: string,\n): Promise<string | undefined> {\n for (const tenantId of tenantIds) {\n const value = await withTenant({ tenantId }, async () => {\n try {\n return (await service.retrieve(secretName)).value;\n } catch (error) {\n if (isMissingSecretError(error, secretName)) {\n return undefined;\n }\n\n throw error;\n }\n });\n\n if (value) {\n return value;\n }\n }\n\n return undefined;\n}\n\nfunction isMissingSecretError(error: unknown, secretName: string): boolean {\n if (!(error instanceof Error)) {\n return false;\n }\n\n return (\n error.message === `Secret '${secretName}' not found` ||\n error.message === 'Secret not found'\n );\n}\n\nexport async function resolveAgentAIOptions(\n input: ResolveAgentAIOptionsInput,\n): Promise<AIClientOptions | undefined> {\n const { aiConfig, db } = input;\n if (!aiConfig) {\n return undefined;\n }\n\n const normalized = { ...aiConfig };\n if (asNonEmptyString(normalized.apiKey)) {\n return stripAgentAISecretFields(normalized);\n }\n\n const secretName =\n asNonEmptyString(normalized.apiKeySecretName) ??\n getDefaultSecretName(normalized);\n if (!secretName || !db) {\n return stripAgentAISecretFields(normalized);\n }\n\n const tenantId =\n asNonEmptyString(input.tenantId) ??\n asNonEmptyString(getCurrentTenant()?.tenantId);\n if (!tenantId) {\n return stripAgentAISecretFields(normalized);\n }\n\n const fallback = normalizeSecretFallback(normalized.apiKeySecretFallback);\n const tenantIds = await getTenantSearchOrder(db, tenantId, fallback);\n const service = await getSecretService(db);\n const apiKey = await resolveSecretValue(service, tenantIds, secretName);\n\n if (!apiKey) {\n return stripAgentAISecretFields(normalized);\n }\n\n return {\n ...stripAgentAISecretFields(normalized),\n apiKey,\n };\n}\n","import type { SmrtClassOptions, SmrtObject } from '@happyvertical/smrt-core';\n\n// Forward reference for Agent type (avoids circular dependency).\n// The actual Agent class is in agent.ts which imports from this file. We model\n// only the structural surface a handler relies on (the agent's `options`); a\n// concrete class instance is not assignable to a type with a string index\n// signature, so handlers needing richer access should specialize the `A`\n// type parameter with their concrete agent type.\ntype AgentLike = {\n options: SmrtClassOptions;\n};\n\n/**\n * Handler function that processes a single matched interest item\n *\n * Called for each item after filtering/qualification. Use to determine\n * what action to take for each matched item.\n *\n * @param item - The matched SmrtObject\n * @param agent - The agent instance (for accessing agent context/methods)\n * @returns An action descriptor object (or any value)\n *\n * @example\n * ```typescript\n * // Simple action descriptor\n * handler: async (meeting) => ({\n * action: 'recap',\n * meeting\n * })\n *\n * // Using agent context\n * handler: async (meeting, agent) => ({\n * action: 'analyze',\n * config: agent.config,\n * priority: meeting.isUrgent ? 'high' : 'normal'\n * })\n * ```\n */\nexport type InterestHandlerFn<\n T extends SmrtObject = SmrtObject,\n A extends AgentLike = AgentLike,\n R = unknown,\n> = (item: T, agent: A) => Promise<R> | R;\n\n/**\n * Filter object using SDK SQL operator-in-key pattern (AND-only for now)\n *\n * Supports operators in keys:\n * - `{ 'status': 'active' }` → WHERE status = 'active'\n * - `{ 'price >': 100 }` → WHERE price > 100\n * - `{ 'type in': ['a', 'b'] }` → WHERE type IN ('a', 'b')\n *\n * Supported operators: =, >, <, >=, <=, !=, in, like\n */\nexport type ObjectFilter = Record<string, unknown>;\n\n/**\n * Async qualifier function for post-filter processing\n *\n * Receives items after SQL filtering, returns filtered/modified items.\n * Use for filtering that can't be expressed in SQL (e.g., AI-based filtering).\n *\n * @example\n * ```typescript\n * const qualify: AsyncQualifierFn<Meeting> = async (meetings) => {\n * return meetings.filter(m => m.isPublic);\n * };\n * ```\n */\nexport type AsyncQualifierFn<T extends SmrtObject = SmrtObject> = (\n items: T[],\n) => Promise<T[]>;\n\n/**\n * Custom query function for complex SQL patterns\n *\n * Returns a WHERE clause and parameters for use with collection.query().\n * Use for patterns that can't be expressed with standard filters:\n * - NOT EXISTS subqueries\n * - JOINs with other tables\n * - Complex OR conditions\n * - Window functions\n *\n * @param tableName - The main table name (aliased as 't' in the query)\n * @returns Tuple of [whereClause, params] to append to query\n *\n * @example\n * ```typescript\n * // Find meetings without corresponding recaps\n * const query: QueryFn = (t) => [\n * `${t}.start_date < datetime('now') AND NOT EXISTS (\n * SELECT 1 FROM contents c\n * WHERE c.meeting_id = ${t}.id\n * AND c._meta_type = 'MeetingRecap'\n * )`,\n * []\n * ];\n * ```\n */\nexport type QueryFn = (tableName: string) => [sql: string, params: unknown[]];\n\n/**\n * Single interest filter configuration\n *\n * Supports either standard SDK filters OR custom query function, plus\n * optional sort, limit, and post-query qualification.\n */\nexport interface InterestFilter<T extends SmrtObject = SmrtObject> {\n /**\n * Optional label for this interest (useful for debugging/logging)\n */\n name?: string;\n\n /**\n * SQL filter object for queries (standard SDK filter)\n * Merged with global filter using AND logic (object spread)\n *\n * Use this for simple AND conditions with standard operators.\n * For complex queries (NOT EXISTS, JOINs), use `query` instead.\n */\n filter?: ObjectFilter;\n\n /**\n * Custom query function for complex SQL patterns\n *\n * When provided, bypasses standard filter and uses collection.query()\n * with the generated SQL. Supports NOT EXISTS, JOINs, CTEs, etc.\n *\n * Cannot be used together with `filter`.\n */\n query?: QueryFn;\n\n /**\n * SQL orderBy format: 'priority DESC' or ['priority DESC', 'name ASC']\n */\n sort?: string | string[];\n\n /**\n * Maximum number of items to return for this interest\n */\n limit?: number;\n\n /**\n * Async post-filter function on results\n * Runs after SQL query returns, enables AI-based or complex filtering\n */\n qualify?: AsyncQualifierFn<T>;\n\n /**\n * Handler function called for each matched item\n *\n * Use to determine what action to take for each item. The handler\n * receives the item and agent instance, and returns an action descriptor.\n *\n * @example\n * ```typescript\n * handler: async (meeting, agent) => ({\n * action: 'recap',\n * meeting,\n * config: agent.config\n * })\n * ```\n */\n handler?: InterestHandlerFn<T>;\n}\n\n/**\n * Configuration for a specific object type's interest\n *\n * Can be a single InterestFilter or an array of InterestFilters.\n * Arrays allow multiple independent queries for the same object type.\n *\n * @example\n * ```typescript\n * // Single filter (backward compatible)\n * const config: ObjectInterestConfig = {\n * filter: { status: 'active' },\n * sort: 'created_at DESC'\n * };\n *\n * // Multiple filters (new feature)\n * const config: ObjectInterestConfig = [\n * {\n * name: 'needs-analysis',\n * filter: { 'agendaUrl !=': null, status: 'scheduled' }\n * },\n * {\n * name: 'needs-recap',\n * query: (t) => [\n * `${t}.start_date < datetime('now') AND NOT EXISTS (\n * SELECT 1 FROM contents WHERE meeting_id = ${t}.id\n * )`,\n * []\n * ]\n * }\n * ];\n * ```\n */\nexport type ObjectInterestConfig<T extends SmrtObject = SmrtObject> =\n | InterestFilter<T>\n | InterestFilter<T>[];\n\n/**\n * Global interest configuration for an agent\n *\n * @example\n * ```typescript\n * const interests: InterestOptions = {\n * filter: { status: 'active' },\n * sort: 'created_at DESC',\n * objects: {\n * Meeting: {\n * sort: 'scheduled_at DESC',\n * filter: { 'scheduled_at >': new Date() },\n * limit: 10\n * },\n * Document: {\n * filter: { 'type in': ['agenda', 'minutes'] }\n * }\n * }\n * };\n * ```\n */\nexport interface InterestOptions {\n /**\n * Global sort applied to final combined results\n * If not specified, results are grouped by type with type-specific sorts\n */\n sort?: string | string[];\n\n /**\n * Global filter applied to all object types\n * Merged with object-specific filters using AND logic\n */\n filter?: ObjectFilter;\n\n /**\n * Global async qualifier applied after all object-specific qualifiers\n */\n qualify?: AsyncQualifierFn;\n\n /**\n * Object-specific interest configurations\n * Keys must match ObjectRegistry class names (case-insensitive lookup)\n */\n objects: {\n [className: string]: ObjectInterestConfig;\n };\n}\n\n/**\n * Result item from interesting() method\n *\n * @example\n * ```typescript\n * const items = await agent.interesting();\n * for (const { type, data, name, handled } of items) {\n * console.log(`${type} from filter \"${name}\": action=${handled?.action}`);\n * }\n * ```\n */\nexport interface InterestResult<\n T extends SmrtObject = SmrtObject,\n R = unknown,\n> {\n /**\n * Object class name from ObjectRegistry\n */\n type: string;\n\n /**\n * The actual SmrtObject instance\n */\n data: T;\n\n /**\n * Name of the filter that matched this item (if specified)\n * Useful for debugging and logging\n */\n name?: string;\n\n /**\n * Result from handler function (if handler was defined)\n * Contains the action descriptor returned by the handler\n */\n handled?: R;\n}\n\n/**\n * Extended agent options including interests\n */\nexport interface AgentWithInterestsOptions {\n /**\n * Interest configuration for this agent\n */\n interests?: InterestOptions;\n}\n\n/**\n * Merge global and object-specific filters via object spread.\n *\n * Non-colliding keys from both filters are combined (effectively AND-ing them\n * in the resulting query). On a key collision the object-specific value\n * **replaces** the global one — `{ ...global, ...object }` — so a per-object\n * filter overrides the global filter for that key. A global safety filter is\n * therefore NOT preserved when an object filter sets the same key; choose\n * distinct keys (or different operators) if both must apply.\n *\n * @param globalFilter - Global filter applied to all types\n * @param objectFilter - Object-specific filter (wins on key collision)\n * @returns Merged filter object\n *\n * @example\n * ```typescript\n * // Distinct keys are combined:\n * mergeFilters({ status: 'active' }, { 'created_at >': date })\n * // Returns: { status: 'active', 'created_at >': date }\n *\n * // Colliding key: the object value replaces the global one:\n * mergeFilters({ status: 'active' }, { status: 'archived' })\n * // Returns: { status: 'archived' }\n * ```\n */\nexport function mergeFilters(\n globalFilter?: ObjectFilter,\n objectFilter?: ObjectFilter,\n): ObjectFilter {\n if (!globalFilter && !objectFilter) return {};\n if (!globalFilter) return { ...objectFilter };\n if (!objectFilter) return { ...globalFilter };\n return { ...globalFilter, ...objectFilter };\n}\n\n/**\n * Normalize sort to array format\n *\n * @param sort - Sort specification (string or array)\n * @returns Array of sort fields\n *\n * @example\n * ```typescript\n * normalizeSort('created_at DESC')\n * // Returns: ['created_at DESC']\n *\n * normalizeSort(['priority DESC', 'name ASC'])\n * // Returns: ['priority DESC', 'name ASC']\n * ```\n */\nexport function normalizeSort(sort?: string | string[]): string[] {\n if (!sort) return [];\n return Array.isArray(sort) ? sort : [sort];\n}\n","/**\n * Opt-in Learning trait configuration for {@link Agent} (#1886).\n *\n * The trait is **off by default**: an agent that does not declare\n * `static learning` behaves byte-for-byte as it does today. Declaring it — with\n * a single `static learning = true` (or a config object) — wires a\n * confidence-scored recall-before / capture-after loop into the agent\n * lifecycle, backed by {@link LearningMemory} from `@happyvertical/smrt-core`.\n *\n * @module\n */\n\nimport type { LearningMemoryConfig } from '@happyvertical/smrt-core';\n\n/**\n * Per-agent learning configuration. All fields are optional; omitted\n * thresholds fall back to {@link LearningMemory}'s defaults (the proven\n * `praeco` values: reuse floor 0.7, success 0.9, failure 0.3).\n */\nexport interface AgentLearningConfig {\n /** Explicit enable flag. Defaults to `true` when a config object is given. */\n enabled?: boolean;\n /**\n * Base memory scope for this agent. Defaults to `agent/<agentType>`.\n * Recall/capture are additionally isolated by the agent instance id (owner),\n * so two tenants running the same agent class never share memory.\n */\n scope?: string;\n /** Reuse floor — recall omits memories below this confidence. Default 0.7. */\n minConfidence?: number;\n /** Confidence a 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 /** Reinforcement blend weight in `[0, 1]`. Default 0.5. */\n reinforcement?: number;\n /** Optional half-life (ms) for time-based confidence decay. */\n decayHalfLifeMs?: number;\n}\n\n/**\n * The `static learning` declaration accepted on an {@link Agent} subclass:\n * `false` (default, off), `true` (on with defaults), or a config object.\n */\nexport type AgentLearningDeclaration = AgentLearningConfig | boolean;\n\n/** Normalised learning settings resolved from a declaration. */\nexport interface ResolvedAgentLearning {\n enabled: boolean;\n scope?: string;\n /** Threshold overrides to pass to `LearningMemory` (only defined keys). */\n memoryConfig: Partial<LearningMemoryConfig>;\n}\n\n/**\n * Resolve a `static learning` declaration into normalised settings.\n *\n * Only keys explicitly set on the declaration are forwarded to\n * `LearningMemory`, so unset thresholds keep the module's defaults rather than\n * clobbering them with `undefined`.\n */\nexport function resolveAgentLearning(\n declaration: AgentLearningDeclaration | undefined,\n): ResolvedAgentLearning {\n if (declaration === undefined || declaration === false) {\n return { enabled: false, memoryConfig: {} };\n }\n if (declaration === true) {\n return { enabled: true, memoryConfig: {} };\n }\n\n const memoryConfig: Partial<LearningMemoryConfig> = {};\n if (declaration.minConfidence !== undefined) {\n memoryConfig.minConfidence = declaration.minConfidence;\n }\n if (declaration.successConfidence !== undefined) {\n memoryConfig.successConfidence = declaration.successConfidence;\n }\n if (declaration.failureConfidence !== undefined) {\n memoryConfig.failureConfidence = declaration.failureConfidence;\n }\n if (declaration.reinforcement !== undefined) {\n memoryConfig.reinforcement = declaration.reinforcement;\n }\n if (declaration.decayHalfLifeMs !== undefined) {\n memoryConfig.decayHalfLifeMs = declaration.decayHalfLifeMs;\n }\n\n return {\n enabled: declaration.enabled ?? true,\n scope: declaration.scope,\n memoryConfig,\n };\n}\n","import type { AIClientOptions } from '@happyvertical/ai';\nimport { createLogger, type Logger } from '@happyvertical/logger';\nimport { sanitizeConfig } from '@happyvertical/smrt-config';\nimport {\n type ConfigResolver,\n createDispatchBus,\n type DispatchBus,\n type DispatchMetadata,\n type DispatchTenantScope,\n type LearningEpisode,\n LearningMemory,\n type LearningMemoryRecord,\n type LearningOutcome,\n type LearningSemanticSearch,\n ObjectRegistry,\n resolveDispatchTenantScope,\n type SmrtCollection,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport {\n getCurrentTenant,\n TenantScoped,\n tenantId,\n} from '@happyvertical/smrt-tenancy';\nimport { type AgentAIOptions, resolveAgentAIOptions } from './ai-config.js';\nimport { AgentConfig } from './config.js';\nimport {\n instanceScopedSubscriber,\n getAgentClassName as resolveAgentClassName,\n getAgentTypeName as resolveAgentTypeName,\n} from './identity.js';\nimport type {\n AgentWithInterestsOptions,\n InterestFilter,\n InterestOptions,\n InterestResult,\n ObjectFilter,\n ObjectInterestConfig,\n} from './interests.js';\nimport { mergeFilters, normalizeSort } from './interests.js';\nimport {\n type AgentLearningDeclaration,\n resolveAgentLearning,\n} from './learning.js';\nimport type { AgentStatusType } from './types.js';\nimport type { AgentAdminRoute, AgentUISlots } from './ui.js';\n\n/**\n * Agent constructor options\n */\nexport interface AgentOptions\n extends SmrtObjectOptions,\n AgentWithInterestsOptions {\n /**\n * Optional AI configuration for this agent.\n *\n * When `apiKey` is omitted, the runtime can resolve provider credentials from\n * tenant secrets based on the active tenant context.\n */\n ai?: AgentAIOptions;\n /**\n * Suppress all log output (useful for CLI --json mode)\n * When true, creates a no-op logger that discards all messages\n */\n silent?: boolean;\n /**\n * Opt into process-level SIGTERM/SIGINT handling for this instance.\n *\n * Host runtimes should generally own process lifecycle; this remains available\n * for single-agent CLIs and scripts that explicitly want it. Do not enable\n * this for multiple agents in the same process unless the host coordinates\n * shutdown itself; the first handler to finish exits the process.\n */\n manageProcessSignals?: boolean;\n\n /**\n * Durable per-instance key for multi-instance agents (#1890).\n *\n * Only honored when the agent class opts into multi-instance\n * (`static multiInstance = true`); a singleton agent (the default) ignores it,\n * so passing a key can never change a non-opted agent's behavior. When honored\n * it becomes the per-instance dispatch subscriber suffix and memory partition\n * (see {@link Agent.getDispatchSubscriber} / {@link Agent.learningScope}) so N\n * instances of one class run independently. Typically the persona id from\n * `@happyvertical/smrt-personas` (a persona is a durable instance).\n */\n instanceKey?: string | null;\n\n /**\n * Durable persona row that owns this agent's editable settings.\n *\n * This is deliberately independent from `instanceKey`: the reserved default\n * persona keeps the singleton runtime identity (`instanceKey: null`) but must\n * still load and save its own persona-scoped settings.\n */\n personaId?: string | null;\n}\n\n/**\n * Base Agent class for building autonomous actors in the SMRT ecosystem\n *\n * Agents are SmrtObjects that perform specific tasks with:\n * - Status tracking (idle, initializing, running, error, shutdown)\n * - Configuration management via @have/config\n * - Structured logging via @happyvertical/logger\n * - Lifecycle hooks (initialize, validate, run, shutdown)\n * - Optional process signal handling for graceful shutdown\n *\n * Agents can define their own properties for state management - since they extend\n * SmrtObject, any properties defined will be automatically persisted to the database.\n *\n * **Important**: Extending classes must add the `@smrt()` decorator themselves\n * to configure CLI/API/MCP exposure.\n *\n * @example\n * ```typescript\n * import { Agent } from '@have/agents';\n * import { getModuleConfig } from '@have/config';\n * import { smrt } from '@happyvertical/smrt-core';\n *\n * @smrt()\n * class MyAgent extends Agent {\n * protected config = getModuleConfig('my-agent', {\n * cronSchedule: '0 2 * * *',\n * maxRetries: 3\n * });\n *\n * // Define your own state properties (automatically persisted)\n * lastCrawl: Date | null = null;\n * itemsProcessed: number = 0;\n *\n * async validate(): Promise<void> {\n * if (!this.config.cronSchedule) {\n * throw new Error('cronSchedule is required');\n * }\n * }\n *\n * async run(): Promise<void> {\n * // Agent logic here\n * this.itemsProcessed = 42;\n * this.lastCrawl = new Date();\n * await this.save(); // Persist state\n * }\n * }\n *\n * const agent = new MyAgent({ name: 'my-agent' });\n * await agent.execute();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n // Abstract class - no direct CLI/API/MCP exposure\n // But must be registered for inheritance chain to work (issue #523)\n cli: false,\n api: false,\n mcp: false,\n // STI: All agents share 'agents' table for polymorphic queries\n tableStrategy: 'sti',\n})\nexport abstract class Agent extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global agents\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * UI slots this agent supports for admin panels\n *\n * Subclasses override this to declare their admin UI slots.\n * Each slot can be implemented by a Svelte component.\n *\n * @example\n * ```typescript\n * static override uiSlots: AgentUISlots = {\n * sources: {\n * id: 'sources',\n * label: 'News Sources',\n * description: 'Configure scrapers and data sources',\n * icon: 'database',\n * order: 1,\n * },\n * settings: {\n * id: 'settings',\n * label: 'Agent Settings',\n * description: 'Configure agent behavior',\n * icon: 'settings',\n * order: 2,\n * },\n * };\n * ```\n */\n static uiSlots: AgentUISlots = {};\n\n /**\n * Admin routes this agent provides\n *\n * Subclasses override this to declare admin route metadata.\n * The vitePluginAgentRoutes Vite plugin reads these from the manifest\n * and registers them so host applications can discover and render them.\n *\n * @example\n * ```typescript\n * static override adminRoutes: AgentAdminRoute[] = [\n * { path: 'sources', component: 'SourcesPanel', load: 'loadSources' },\n * { path: 'sources/[sourceId]', component: 'SourceDetail', load: 'loadSourceDetail' },\n * ];\n * ```\n */\n static adminRoutes: AgentAdminRoute[] = [];\n\n /**\n * Signal types this agent subscribes to by default\n *\n * These are seedable defaults — on `initialize()`, the agent checks the\n * database first and only creates subscriptions that don't already exist.\n * The database is the runtime source of truth, allowing users to customize\n * subscriptions per-tenant via the dashboard without code changes.\n *\n * When declared, `execute()` will automatically call `processDispatches()`\n * before `run()`, so handler agents don't need to manually poll.\n * Override `handleDispatch()` to process incoming dispatches.\n *\n * @example\n * ```typescript\n * @smrt({ agent: { icon: 'mail', tier: 'standard' } })\n * class EmailHandler extends Agent {\n * static override signalSubscriptions = ['email.received', 'email.bounced'];\n *\n * async handleDispatch(payload: unknown, metadata: DispatchMetadata) {\n * // Called automatically during execute() for each pending dispatch\n * }\n *\n * async run() { ... }\n * }\n * ```\n */\n static signalSubscriptions: string[] = [];\n\n /**\n * Execute-time resolvers for `agent_config` fields that should be computed\n * lazily rather than snapshotted at sync time.\n *\n * Each entry is keyed by the agent_config field it produces. The runtime\n * (see {@link resolveLazyConfig}) calls every resolver and overlays the\n * results on top of the persisted config before constructing the agent.\n * That means env-derived values like asset storage paths, S3 buckets, AI\n * provider keys, or tenant-scoped DB URLs stay live: rotating an env var\n * is reflected on the next scheduled run without rewriting the schedule\n * row.\n *\n * Resolvers may be sync or async. Returning `undefined` or `null` leaves\n * the persisted value in place — both are treated as \"no overlay\" so the\n * common `() => process.env.X ?? null` pattern is safe and won't clobber\n * a snapshotted value when the env var is unset. Throwing falls back to\n * the persisted value (or to whatever\n * {@link ResolveLazyConfigOptions.onError} dictates).\n *\n * @example\n * ```typescript\n * class Praeco extends Agent {\n * static override configResolvers = {\n * assetStorage: () => resolveSharedAssetStorage(),\n * aiKey: async () => loadAIKeyFromSecretsManager(),\n * };\n * }\n * ```\n */\n static configResolvers: Record<string, ConfigResolver> = {};\n\n /**\n * Opt-in learning trait declaration (#1886).\n *\n * **Off by default.** Set to `true` (or a config object) on a subclass to\n * wire a confidence-scored recall-before / capture-after loop into the agent\n * lifecycle, backed by {@link LearningMemory}. A non-opted agent behaves\n * byte-for-byte as it does today — the learning branches are never entered.\n *\n * When enabled, the loop wraps `run()` itself (in {@link initialize}), so it\n * fires whether the agent runs via {@link execute} or the background/scheduled\n * path (which calls `run()` directly). Each run:\n * 1. recalls confident memories for {@link learningScope} before `run()`,\n * exposing them via {@link recalledMemories};\n * 2. captures the run outcome after `run()` — a clean completion reinforces\n * the staged memory (see {@link stageLearning}); a thrown error or an\n * explicit {@link reportLearningOutcome} failure decays it.\n *\n * @example\n * ```typescript\n * @smrt()\n * class InvoiceAgent extends Agent {\n * static override learning = true; // reuse floor 0.7, success 0.9, fail 0.3\n * // or: static override learning = { minConfidence: 0.8, scope: 'invoices' };\n * protected config = {};\n * async run() {\n * const [cached] = this.recalledMemories;\n * const strategy = cached?.value ?? (await this.generateStrategy());\n * this.stageLearning({ scope: this.learningScope(), key: 'default', value: strategy });\n * }\n * }\n * ```\n */\n static learning: AgentLearningDeclaration = false;\n\n /**\n * Opt into multiple durable instances of this agent class per tenant (#1890).\n *\n * **Off by default** — a non-opted class is a **singleton** (the N=1 case) and\n * behaves byte-for-byte as it does today: one dispatch subscriber keyed by the\n * agent type, one memory scope, class-wide interests. Setting this to `true`\n * lets N configured instances (personas, from `@happyvertical/smrt-personas`)\n * run independently: each is constructed with its own {@link AgentOptions.instanceKey},\n * which the framework folds into a per-instance dispatch subscriber\n * ({@link getDispatchSubscriber}), memory partition ({@link learningScope}),\n * and interest/subscription scoping seams ({@link instanceInterestFilter} /\n * {@link resolveSignalSubscriptions}) so two instances never double-process\n * each other's dispatches or interests.\n *\n * The framework provides the per-instance *identity*; a package scopes its own\n * dispatch/interests to the instance's config by overriding the seams. The\n * `default` persona reuses the singleton identity (null key), which makes the\n * singleton→multi upgrade non-destructive.\n */\n static multiInstance: boolean = false;\n\n /**\n * Current agent status\n */\n status: AgentStatusType = 'idle';\n\n /**\n * Structured logger instance\n * Created with agent's class name as context\n */\n protected logger: Logger;\n\n /**\n * Agent configuration\n * Must be defined by extending classes using getModuleConfig()\n *\n * @example\n * ```typescript\n * protected config = getModuleConfig('my-agent', {\n * cronSchedule: '0 0 * * *',\n * maxRetries: 3\n * });\n * ```\n */\n protected abstract config: unknown;\n\n /**\n * Signal handlers for graceful shutdown\n */\n private signalHandlers: Map<NodeJS.Signals, () => void> = new Map();\n\n /**\n * Cached DispatchBus instance for inter-agent communication\n */\n private _dispatch: DispatchBus | null = null;\n\n /**\n * Cached LearningMemory binding, once successfully built. Not cached when\n * learning is disabled or the DB isn't ready yet, so an early call can't\n * permanently stick the agent in a learning-disabled state.\n */\n private _learningMemory?: LearningMemory;\n\n /**\n * Whether `run()` has been wrapped with the learning loop (idempotency guard).\n */\n private _runWrappedForLearning = false;\n\n /**\n * The episode the current run acted on, staged via {@link stageLearning} so\n * the lifecycle can reinforce it after `run()`.\n */\n private _learningEpisode: LearningEpisode | null = null;\n\n /**\n * Explicit outcome for the current run, set via\n * {@link reportLearningOutcome}. When unset, a clean `run()` is treated as a\n * success and a thrown error as a failure.\n */\n private _learningOutcome: LearningOutcome | null = null;\n\n /**\n * Memories recalled before `run()` when the learning trait is enabled.\n *\n * Empty for non-opted agents. Populated by the lifecycle (see\n * {@link recallForRun}); read from `run()` to reuse prior knowledge.\n */\n protected recalledMemories: LearningMemoryRecord[] = [];\n\n /**\n * Creates a new Agent instance\n *\n * @param options - Configuration options including identifiers and metadata\n */\n constructor(options: AgentOptions = {}) {\n super(options);\n // Use no-op logger in silent mode (for CLI --json output)\n this.logger = createLogger(options.silent ? false : { level: 'info' });\n }\n\n /**\n * Interest configuration for this agent\n * Lazily accessed from options on first interesting() call\n */\n protected get interests(): InterestOptions | undefined {\n return (this.options as AgentOptions).interests;\n }\n\n /**\n * Canonical agent type for persistence and dispatch routing.\n */\n protected getAgentTypeName(): string {\n const metaType = (this as { _meta_type?: unknown })._meta_type;\n if (typeof metaType === 'string' && metaType.length > 0) {\n return resolveAgentTypeName(metaType);\n }\n\n return resolveAgentTypeName(this.constructor.name);\n }\n\n /**\n * Human-readable class name for logs and UI.\n */\n protected getAgentClassName(): string {\n return resolveAgentClassName(this.getAgentTypeName());\n }\n\n // ============================================================================\n // Multi-instance identity (#1890) — opt-in; singleton (null key) by default\n // ============================================================================\n\n /**\n * Whether this agent class opted into multiple durable instances per tenant.\n */\n protected isMultiInstance(): boolean {\n return (this.constructor as typeof Agent).multiInstance === true;\n }\n\n /**\n * The durable per-instance key for this agent, or `null` for a singleton.\n *\n * Returns `null` unless the class opts in (`static multiInstance = true`) AND a\n * non-empty {@link AgentOptions.instanceKey} was supplied — so a non-opted\n * agent is always singleton-identified even if a key is passed. This is the\n * anchor the framework folds into the dispatch subscriber, memory scope, and\n * scoping seams below.\n */\n getInstanceKey(): string | null {\n if (!this.isMultiInstance()) {\n return null;\n }\n const key = (this.options as AgentOptions).instanceKey;\n return typeof key === 'string' && key.length > 0 ? key : null;\n }\n\n /**\n * Durable owner id used for database-backed slot configuration.\n *\n * Persona-backed agents use the persona row id, including the default\n * persona whose runtime instance key remains null. Legacy/singleton agents\n * continue to use their persisted Agent STI row id.\n */\n getConfigOwnerId(slotId?: string): string | null {\n const personaId = (this.options as AgentOptions).personaId;\n const scope = slotId ? this.getUISlots()[slotId]?.scope : undefined;\n if (scope === 'persona') {\n return typeof personaId === 'string' && personaId.length > 0\n ? personaId\n : null;\n }\n if (scope === 'agent') {\n return this.id ?? null;\n }\n if (typeof personaId === 'string' && personaId.length > 0) {\n return personaId;\n }\n return this.id ?? null;\n }\n\n /**\n * Canonical dispatch subscriber identity for this agent.\n *\n * A singleton (no instance key) is the bare agent type — **unchanged** from the\n * class-keyed behavior. A multi-instance agent is `` `${agentType}#${key}` ``,\n * giving each instance its own subscription rows and its own pending-dispatch\n * queue so instances don't compete for or double-process each other's\n * dispatches. Used everywhere the agent subscribes, seeds, and processes.\n */\n getDispatchSubscriber(): string {\n return instanceScopedSubscriber(\n this.getAgentTypeName(),\n this.getInstanceKey(),\n );\n }\n\n /**\n * The signal types this instance should seed as dispatch subscriptions.\n *\n * Defaults to the class's static {@link Agent.signalSubscriptions} unchanged.\n * A multi-instance package overrides this to derive **instance-scoped** signal\n * types from the persona/instance config (e.g. append the instance key or a\n * routing dimension), so an emit meant for one instance only matches that\n * instance's subscription and the other never processes it.\n */\n protected resolveSignalSubscriptions(): string[] {\n return (this.constructor as typeof Agent).signalSubscriptions;\n }\n\n /**\n * An optional filter AND-merged (as the base layer) into every\n * {@link interesting} query for this instance.\n *\n * `undefined` by default (no scoping — singleton behavior unchanged). A\n * multi-instance package overrides it to return an instance-discriminating\n * filter derived from the persona/instance config, so two instances of one\n * class partition the objects they process and never double-handle the same\n * row. Global and per-object interest filters layer on top (and win on key\n * collision), so choose a dedicated discriminator key here.\n *\n * Applies to the standard filter path; custom `query` interest filters own\n * their SQL and should incorporate {@link getInstanceKey} themselves.\n */\n protected instanceInterestFilter(): ObjectFilter | undefined {\n return undefined;\n }\n\n /**\n * Get UI slot definitions for this agent instance\n *\n * Returns the static uiSlots defined on the agent's class.\n * Used by host applications to discover available admin panels.\n *\n * @example\n * ```typescript\n * const slots = agent.getUISlots();\n * for (const [slotId, slot] of Object.entries(slots)) {\n * console.log(`${slot.label}: ${slot.description}`);\n * }\n * ```\n */\n getUISlots(): AgentUISlots {\n return (this.constructor as typeof Agent).uiSlots;\n }\n\n // ============================================================================\n // Configuration Management\n // ============================================================================\n\n /**\n * Load all database-persisted configs for this agent\n *\n * Returns a Map of slotId → configData for all saved configurations.\n * Use getMergedConfig() to get file + db merged config for a slot.\n *\n * @returns Map of slotId to config data\n *\n * @example\n * ```typescript\n * const configs = await agent.loadConfigs();\n * const sources = configs.get('sources');\n * ```\n */\n async loadConfigs(): Promise<Map<string, Record<string, unknown>>> {\n const ownerIds = Array.from(\n new Set(\n [\n this.getConfigOwnerId(),\n this.id ?? null,\n (this.options as AgentOptions).personaId ?? null,\n ].filter((id): id is string => typeof id === 'string' && id.length > 0),\n ),\n );\n if (ownerIds.length === 0) {\n throw new Error(\n 'Agent must have a personaId or be saved before loading configs',\n );\n }\n const byOwner = await AgentConfig.forAgents(ownerIds, this.options);\n const result = new Map<string, Record<string, unknown>>();\n for (const [ownerId, configs] of byOwner) {\n for (const [slotId, config] of configs) {\n if (this.getConfigOwnerId(slotId) === ownerId) {\n result.set(slotId, config);\n }\n }\n }\n return result;\n }\n\n /**\n * Save config for a specific UI slot to the database\n *\n * Persists configuration data that can be modified by admin panels.\n * Use this when the user saves changes in an admin UI.\n *\n * @param slotId - The UI slot ID (e.g., 'sources', 'settings')\n * @param data - Configuration data to save\n *\n * @example\n * ```typescript\n * await agent.saveSlotConfig('sources', {\n * scrapers: ['civicweb', 'govstack'],\n * refreshInterval: 3600\n * });\n * ```\n */\n async saveSlotConfig(\n slotId: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n const ownerId = this.getConfigOwnerId(slotId);\n if (!ownerId) {\n throw new Error(\n 'Agent must have a personaId or be saved before saving slot config',\n );\n }\n await AgentConfig.saveSlot(\n {\n agentId: ownerId,\n agentClass: this.getAgentTypeName(),\n slotId,\n configData: data,\n },\n this.options,\n );\n }\n\n /**\n * Get merged config for a slot (file-based + database)\n *\n * Priority order (highest to lowest):\n * 1. Database-persisted config (from saveSlotConfig)\n * 2. File-based config (from getModuleConfig)\n * 3. Agent class defaults\n *\n * @param slotId - The UI slot ID\n * @returns Merged configuration object\n *\n * @example\n * ```typescript\n * const sourcesConfig = await agent.getMergedConfig('sources');\n * // Returns file config merged with any db overrides\n * ```\n */\n async getMergedConfig(slotId: string): Promise<Record<string, unknown>> {\n // Get file-based config from module config\n const fileConfig =\n ((this.config as Record<string, unknown>)?.[slotId] as\n | Record<string, unknown>\n | undefined) ?? {};\n\n const ownerId = this.getConfigOwnerId(slotId);\n if (!ownerId) {\n return fileConfig;\n }\n\n // Get db-persisted config\n const dbConfig = await AgentConfig.forSlot(ownerId, slotId, this.options);\n\n // Merge: db overrides file\n return { ...fileConfig, ...(dbConfig ?? {}) };\n }\n\n /**\n * Export all config for this agent (for static site generation)\n *\n * Merges file-based and database configs, then optionally sanitizes\n * to remove secrets. Use this before building a static site.\n *\n * @param options - Export options\n * @param options.includeSecrets - If true, includes API keys and secrets (default: false)\n * @returns Merged configuration object\n *\n * @example\n * ```typescript\n * // Export for static build (secrets filtered)\n * const config = await agent.exportConfig();\n *\n * // Export with secrets (for secure environments)\n * const fullConfig = await agent.exportConfig({ includeSecrets: true });\n * ```\n */\n async exportConfig(options?: {\n includeSecrets?: boolean;\n }): Promise<Record<string, unknown>> {\n const dbConfigs = await this.loadConfigs();\n const fileConfig = (this.config as Record<string, unknown>) ?? {};\n\n // Merge all configs\n const merged: Record<string, unknown> = { ...fileConfig };\n for (const [slotId, data] of dbConfigs) {\n merged[slotId] = {\n ...(merged[slotId] as Record<string, unknown> | undefined),\n ...data,\n };\n }\n\n // Sanitize if secrets not included (uses centralized sanitizeConfig from smrt-config)\n if (!options?.includeSecrets) {\n return sanitizeConfig(merged) as Record<string, unknown>;\n }\n\n return merged;\n }\n\n /**\n * Get the DispatchBus for inter-agent communication\n *\n * Creates a DispatchBus lazily on first access. Requires database configuration.\n *\n * @example\n * ```typescript\n * // Emit a dispatch to other agents\n * await this.dispatch.emit('campaign.completed', {\n * campaignId: '123',\n * revenue: 5000\n * }, { source: this.constructor.name });\n *\n * // Subscribe to dispatches\n * await this.dispatch.subscribe({\n * signalType: 'campaign.*',\n * subscriber: this.constructor.name\n * });\n * ```\n *\n * @throws Error if database is not configured\n */\n async getDispatch(): Promise<DispatchBus> {\n if (!this._dispatch) {\n if (!this._db) {\n throw new Error(\n `Agent ${this.constructor.name} requires database configuration for dispatch. ` +\n `Ensure the agent is initialized with a db option.`,\n );\n }\n this._dispatch = await createDispatchBus({\n db: this._db,\n });\n }\n return this._dispatch;\n }\n\n /**\n * Handle incoming dispatches\n *\n * Override this method to process dispatches targeted at this agent.\n * Called when process() is invoked for this agent's subscriber name.\n *\n * @param payload - Dispatch payload data\n * @param metadata - Dispatch metadata including type, source, and timing\n *\n * @example\n * ```typescript\n * async handleDispatch(payload: unknown, metadata: DispatchMetadata): Promise<void> {\n * if (metadata.type === 'campaign.completed') {\n * const data = payload as { campaignId: string; revenue: number };\n * await this.recordRevenue(data.campaignId, data.revenue);\n * }\n * }\n * ```\n */\n async handleDispatch(\n _payload: unknown,\n _metadata: DispatchMetadata,\n ): Promise<void> {\n // Default implementation does nothing\n // Subclasses should override to process dispatches\n }\n\n /**\n * Process pending dispatches for this agent\n *\n * Finds and processes all pending dispatches that match this agent's subscriptions.\n * Uses handleDispatch() to process each dispatch.\n *\n * @returns Number of dispatches processed\n *\n * @example\n * ```typescript\n * // In your run() method\n * const processed = await this.processDispatches();\n * this.logger.info(`Processed ${processed} dispatches`);\n * ```\n */\n async processDispatches(): Promise<number> {\n const dispatch = await this.getDispatch();\n return dispatch.process(\n this.getDispatchSubscriber(),\n this.handleDispatch.bind(this),\n );\n }\n\n // ============================================================================\n // Learning Trait (#1886) — opt-in; inert unless `static learning` is set\n // ============================================================================\n\n /**\n * Base memory scope for this agent's learning.\n *\n * Defaults to the configured `scope` (if any) or `agent/<agentType>`.\n * Override to shape how memories are filed (e.g. per task type). Recall and\n * capture are additionally isolated by the agent instance id (owner), so\n * memory never bleeds across tenants running the same agent class.\n */\n protected learningScope(): string {\n const resolved = resolveAgentLearning(\n (this.constructor as typeof Agent).learning,\n );\n const base = resolved.scope ?? `agent/${this.getAgentTypeName()}`;\n // Partition memory per durable instance so two multi-instance personas learn\n // independently. Null key (singleton) leaves the scope unchanged.\n const instanceKey = this.getInstanceKey();\n return instanceKey ? `${base}#${instanceKey}` : base;\n }\n\n /**\n * Optional semantic-search arm for {@link LearningMemory}.\n *\n * Returns `undefined` by default (keyed-context recall only). Override to\n * wire embedding search — e.g. return a bound `collection.semanticSearch`.\n */\n protected getLearningSemanticSearch(): LearningSemanticSearch | undefined {\n return undefined;\n }\n\n /**\n * Resolve the tenant id used for the learning scope and semantic filtering.\n */\n private resolveLearningTenantId(): string | null {\n const contextTenant = getCurrentTenant()?.tenantId;\n if (typeof contextTenant === 'string') return contextTenant;\n return typeof this.tenantId === 'string' ? this.tenantId : null;\n }\n\n /**\n * Get this agent's {@link LearningMemory} binding, or `null` when learning is\n * disabled or no database is configured.\n *\n * Cheap and side-effect-free when the trait is off (returns `null` after a\n * single static-flag check), which keeps non-opted agents unchanged.\n */\n getLearningMemory(): LearningMemory | null {\n if (this._learningMemory) {\n return this._learningMemory;\n }\n\n const resolved = resolveAgentLearning(\n (this.constructor as typeof Agent).learning,\n );\n // Disabled is a stable answer (cheap static check, no need to cache). When\n // enabled but the DB isn't wired yet, return null WITHOUT caching so a later\n // call (after initialize()) can build the binding.\n if (!resolved.enabled || !this._db) {\n return null;\n }\n\n // Ensure a stable owner id so memory is bound to this instance.\n if (!this.id) {\n this.id = crypto.randomUUID();\n }\n\n this._learningMemory = new LearningMemory({\n db: this.systemDb,\n ownerClass: this.getAgentTypeName(),\n ownerId: this.id as string,\n tenantId: this.resolveLearningTenantId(),\n semanticSearch: this.getLearningSemanticSearch(),\n config: resolved.memoryConfig,\n });\n return this._learningMemory;\n }\n\n /**\n * Wrap `run()` with the recall-before / capture-after learning loop when the\n * trait is enabled, so it fires **however run() is invoked** — via\n * {@link execute} OR directly by the background/scheduled path\n * (`ScheduleRunner` → `TaskRunner` calls the agent's configured method, which\n * defaults to `run` and never goes through `execute()`). Both paths call\n * {@link initialize}, so wrapping here covers them. Idempotent, and a no-op\n * for non-opted agents (their `run()` is left untouched).\n */\n private wrapRunForLearning(): void {\n if (this._runWrappedForLearning) return;\n if (\n !resolveAgentLearning((this.constructor as typeof Agent).learning).enabled\n ) {\n return;\n }\n this._runWrappedForLearning = true;\n\n const originalRun = this.run.bind(this);\n (this as { run: () => Promise<void> }).run = async (): Promise<void> => {\n const memory = this.getLearningMemory();\n if (!memory) {\n await originalRun();\n return;\n }\n\n // Clear per-run learning state up front so a throw in recallForRun()\n // can't leave stale recalled memories from a previous run.\n this.recalledMemories = [];\n this._learningEpisode = null;\n this._learningOutcome = null;\n try {\n this.recalledMemories = await this.recallForRun(memory);\n await originalRun();\n await this.captureForRun(\n memory,\n this._learningOutcome ?? { success: true },\n );\n } catch (error) {\n // Capture the failure, but never mask the original error.\n try {\n await this.captureForRun(memory, {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n });\n } catch (captureError) {\n this.logger.warn('Learning capture failed during error handling', {\n error: captureError,\n });\n }\n throw error;\n } finally {\n this._learningEpisode = null;\n this._learningOutcome = null;\n }\n };\n }\n\n /**\n * Recall relevant memories before `run()`.\n *\n * Default: a scope-wide, confidence-filtered recall of {@link learningScope}.\n * Override to shape the recall (e.g. a keyed lookup or a semantic query).\n */\n protected async recallForRun(\n memory: LearningMemory,\n ): Promise<LearningMemoryRecord[]> {\n return memory.recall(this.learningScope());\n }\n\n /**\n * Capture the run outcome after `run()`.\n *\n * Default: reinforce the memory staged via {@link stageLearning}. A no-op\n * when nothing was staged. Override for bespoke capture logic.\n */\n protected async captureForRun(\n memory: LearningMemory,\n outcome: LearningOutcome,\n ): Promise<void> {\n if (!this._learningEpisode) return;\n await memory.capture(this._learningEpisode, outcome);\n }\n\n /**\n * Stage the memory episode the current run acted on, so the lifecycle\n * reinforces it after `run()` completes. Call from `run()`.\n */\n protected stageLearning(episode: LearningEpisode): void {\n this._learningEpisode = episode;\n }\n\n /**\n * Report an explicit outcome for the current run (e.g. a validated failure\n * that did not throw). Overrides the default success/throw inference.\n */\n protected reportLearningOutcome(outcome: LearningOutcome): void {\n this._learningOutcome = outcome;\n }\n\n /**\n * Initialize the agent\n * Sets status to 'initializing' and sets up signal handlers\n *\n * Override to perform setup after construction, but always call super.initialize()\n *\n * @example\n * ```typescript\n * async initialize(): Promise<void> {\n * await super.initialize();\n * // Custom initialization logic\n * }\n * ```\n */\n async initialize(): Promise<this> {\n await super.initialize();\n this.status = 'initializing';\n this.logger.info('Agent initializing');\n\n const fileAiConfig =\n typeof this.config === 'object' &&\n this.config !== null &&\n 'ai' in (this.config as Record<string, unknown>) &&\n typeof (this.config as Record<string, unknown>).ai === 'object' &&\n (this.config as Record<string, unknown>).ai !== null\n ? ((this.config as Record<string, unknown>).ai as AgentAIOptions)\n : undefined;\n const configuredAi =\n ((this.options as AgentOptions).ai as AgentAIOptions | undefined) ??\n fileAiConfig;\n if (configuredAi && this._db) {\n const resolvedAi = await resolveAgentAIOptions({\n aiConfig: configuredAi,\n db: this._db,\n tenantId:\n getCurrentTenant()?.tenantId ||\n (typeof this.tenantId === 'string' ? this.tenantId : undefined),\n });\n if (resolvedAi) {\n (this.options as AgentOptions).ai = resolvedAi as AIClientOptions &\n Record<string, unknown>;\n }\n }\n\n if ((this.options as AgentOptions).manageProcessSignals) {\n this.setupSignalHandlers();\n }\n\n // Seed declarative signal subscriptions (DB is source of truth)\n if (this._db) {\n const dispatch = await this.getDispatch();\n await this.migrateLegacyDispatchSubscriptions(dispatch);\n\n const subs = this.resolveSignalSubscriptions();\n if (subs.length > 0) {\n const subscriber = this.getDispatchSubscriber();\n const existing = await dispatch.listSubscriptions(subscriber);\n const existingTypes = new Set(existing.map((s) => s.signalType));\n for (const signalType of subs) {\n if (!existingTypes.has(signalType)) {\n await dispatch.subscribe({\n signalType,\n subscriber,\n });\n }\n }\n }\n }\n\n // Engage the learning loop around run() (opt-in; no-op otherwise). Done\n // here — not only in execute() — so the background/scheduled path, which\n // calls initialize() then run() directly, learns too.\n this.wrapRunForLearning();\n\n return this;\n }\n\n /**\n * Set up signal handlers for graceful shutdown\n * Handles SIGTERM and SIGINT for single-agent processes that explicitly opt in.\n */\n private setupSignalHandlers(): void {\n const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n\n for (const signal of signals) {\n const handler = () => {\n this.logger.info(`Received ${signal}, shutting down gracefully`);\n this.shutdown()\n .then(() => {\n process.exit(0);\n })\n .catch((error) => {\n this.logger.error('Error during shutdown', { error });\n process.exit(1);\n });\n };\n\n this.signalHandlers.set(signal, handler);\n process.on(signal, handler);\n }\n }\n\n /**\n * Migrate legacy simple-name dispatch subscribers to the canonical agent type.\n *\n * Older releases used `this.constructor.name` directly for subscriber IDs.\n * That collides across packages and leaves fan-out dispatches targeted at the\n * wrong subscriber once qualified names are available.\n */\n private async migrateLegacyDispatchSubscriptions(\n dispatch: DispatchBus,\n ): Promise<void> {\n if (!this._db) {\n return;\n }\n\n const legacySubscriber = this.constructor.name;\n const canonicalSubscriber = this.getAgentTypeName();\n\n if (legacySubscriber === canonicalSubscriber) {\n return;\n }\n\n const legacySubscriptions =\n await dispatch.listSubscriptions(legacySubscriber);\n if (legacySubscriptions.length === 0) {\n return;\n }\n\n const currentSubscriptions =\n await dispatch.listSubscriptions(canonicalSubscriber);\n const currentSignalTypes = new Set(\n currentSubscriptions.map((sub) => sub.signalType),\n );\n\n for (const subscription of legacySubscriptions) {\n if (!currentSignalTypes.has(subscription.signalType)) {\n await dispatch.subscribe({\n signalType: subscription.signalType,\n subscriber: canonicalSubscriber,\n handler: subscription.handler,\n delivery: subscription.delivery,\n enabled: subscription.enabled,\n });\n }\n\n await dispatch.unsubscribe(subscription.signalType, legacySubscriber);\n }\n\n // Tenant isolation (S5 #1398): the bus's subscribe/unsubscribe calls above\n // are tenant-scoped server-side, but this raw UPDATE reaches around the bus\n // directly into `_smrt_dispatch`. Without a tenant predicate it would\n // rewrite the target/processor of EVERY tenant's dispatch rows matching the\n // legacy subscriber name, letting an agent under one tenant retarget another\n // tenant's pending dispatches. Derive the active scope server-side (never\n // from caller input) and restrict the UPDATE to the rows the bus would let\n // this scope read/claim.\n const [tenantClause, tenantParams] = buildDispatchTenantUpdatePredicate(\n resolveDispatchTenantScope(),\n );\n\n await this._db.query(\n `UPDATE _smrt_dispatch\n SET target_subscriber = CASE\n WHEN target_subscriber = ? THEN ?\n ELSE target_subscriber\n END,\n processed_by = CASE\n WHEN processed_by = ? THEN ?\n ELSE processed_by\n END\n WHERE (target_subscriber = ? OR processed_by = ?)${tenantClause}`,\n legacySubscriber,\n canonicalSubscriber,\n legacySubscriber,\n canonicalSubscriber,\n legacySubscriber,\n legacySubscriber,\n ...tenantParams,\n );\n }\n\n /**\n * Clean up signal handlers\n */\n private cleanupSignalHandlers(): void {\n for (const [signal, handler] of this.signalHandlers.entries()) {\n process.removeListener(signal, handler);\n }\n this.signalHandlers.clear();\n }\n\n /**\n * Validate configuration and dependencies\n * Override to check agent-specific requirements\n *\n * @throws Error if validation fails\n *\n * @example\n * ```typescript\n * async validate(): Promise<void> {\n * if (!this.config.apiKey) {\n * throw new Error('API key is required');\n * }\n * }\n * ```\n */\n async validate(): Promise<void> {\n this.logger.info('Validating agent configuration');\n // Base implementation - extending agents should override\n }\n\n /**\n * Main agent logic\n * Must be implemented by extending class\n *\n * Update this.lastRun.itemsProcessed to track work done\n *\n * @example\n * ```typescript\n * async run(): Promise<void> {\n * this.logger.info('Starting agent work');\n * let processed = 0;\n *\n * for (const item of items) {\n * await this.processItem(item);\n * processed++;\n * }\n *\n * this.lastRun.itemsProcessed = processed;\n * this.logger.info(`Processed ${processed} items`);\n * }\n * ```\n */\n abstract run(): Promise<void>;\n\n /**\n * Cleanup and shutdown\n * Override to perform graceful shutdown\n *\n * Always call super.shutdown() to clean up signal handlers\n *\n * @example\n * ```typescript\n * async shutdown(): Promise<void> {\n * this.logger.info('Cleaning up resources');\n * await this.cleanup();\n * await super.shutdown();\n * }\n * ```\n */\n async shutdown(): Promise<void> {\n this.status = 'shutdown';\n this.logger.info('Agent shutting down');\n this.cleanupSignalHandlers();\n }\n\n /**\n * Execute agent with lifecycle management\n *\n * Runs the full lifecycle:\n * 1. initialize() — seeds signal subscriptions if declared\n * 2. validate()\n * 3. processDispatches() — auto-processes pending dispatches if subscriptions exist\n * 4. run()\n *\n * Note: handleDispatch() callbacks may fire before run() is entered.\n *\n * On error:\n * 1. Sets status to 'error'\n * 2. Logs error\n * 3. Re-throws error\n *\n * @example\n * ```typescript\n * const agent = new MyAgent({ name: 'my-agent' });\n *\n * try {\n * await agent.execute();\n * console.log('Agent completed successfully');\n * } catch (error) {\n * console.error('Agent failed:', error);\n * }\n * ```\n */\n async execute(): Promise<void> {\n try {\n await this.initialize();\n await this.validate();\n\n this.status = 'running';\n\n // Auto-process pending dispatches for agents with signal subscriptions\n if (this._db) {\n const dispatch = await this.getDispatch();\n const subs = await dispatch.listSubscriptions(\n this.getDispatchSubscriber(),\n );\n if (subs.length > 0) {\n const count = await this.processDispatches();\n if (count > 0) {\n this.logger.info(`Processed ${count} pending dispatches`);\n }\n }\n }\n\n // The learning loop (#1886) is wrapped around run() in initialize(), so\n // recall-before / capture-after fires here and on the scheduled path\n // alike — nothing learning-specific is needed in execute() itself.\n await this.run();\n this.status = 'idle';\n\n this.logger.info('Agent execution completed');\n } catch (error) {\n this.status = 'error';\n this.logger.error('Agent execution failed', { error });\n throw error;\n }\n }\n\n /**\n * Query objects this agent is interested in\n *\n * Returns items from all configured object types, filtered and sorted\n * according to interest configuration. If handlers are defined on filters,\n * they are called for each matched item and the result is included.\n *\n * @returns Array of { type, data, name?, handled? } results\n * @throws Error if no interests are configured\n *\n * @example\n * ```typescript\n * const items = await this.interesting();\n * for (const { type, data, name, handled } of items) {\n * console.log(`Processing ${type} from \"${name}\": action=${handled?.action}`);\n * }\n * ```\n */\n async interesting(): Promise<InterestResult[]> {\n if (!this.interests) {\n throw new Error(\n `Agent ${this.constructor.name} has no interests configured. ` +\n `Set interests in constructor options to use interesting().`,\n );\n }\n\n if (\n !this.interests.objects ||\n Object.keys(this.interests.objects).length === 0\n ) {\n this.logger.warn('Agent has empty interests.objects configuration');\n return [];\n }\n\n const results: InterestResult[] = [];\n\n // Process each object type in interests.objects\n for (const [className, config] of Object.entries(this.interests.objects)) {\n try {\n const items = await this.queryInterestingObjects(className, config);\n results.push(...items);\n } catch (error) {\n // Log warning and continue with other types\n this.logger.warn(`Failed to query ${className} for interests`, {\n error,\n });\n }\n }\n\n // Apply global qualifier if configured\n if (this.interests.qualify) {\n const allItems = results.map((r) => r.data);\n const qualified = await this.interests.qualify(allItems);\n\n // Rebuild results array with only qualified items\n const qualifiedSet = new Set(qualified);\n const filteredResults = results.filter((r) => qualifiedSet.has(r.data));\n\n // Apply global sort if configured\n if (this.interests.sort) {\n return this.sortResults(filteredResults, this.interests.sort);\n }\n return filteredResults;\n }\n\n // Apply global sort if configured (no global qualifier)\n if (this.interests.sort) {\n return this.sortResults(results, this.interests.sort);\n }\n\n return results;\n }\n\n /**\n * Query a single object type based on interest config\n *\n * Supports both single filter and array of filters.\n * Each filter can use standard SDK filters OR custom query function.\n * Returns InterestResult[] with handler results included.\n */\n private async queryInterestingObjects(\n className: string,\n config: ObjectInterestConfig,\n ): Promise<InterestResult[]> {\n // Check if class is registered (case-insensitive)\n if (!ObjectRegistry.hasClass(className)) {\n this.logger.warn(\n `Object type \"${className}\" not found in ObjectRegistry. ` +\n `Skipping in interests query.`,\n );\n return [];\n }\n\n // Get collection for this class type\n const collection = await ObjectRegistry.getCollection(\n className,\n this.options,\n );\n\n // Normalize config to array format\n const filters = this.normalizeInterestConfig(config);\n\n // Query each filter and collect results\n const allResults: InterestResult[] = [];\n\n for (const filter of filters) {\n const items = await this.queryInterestFilter(\n className,\n filter,\n collection,\n );\n\n // Process each item: call handler if defined, build result\n for (const item of items) {\n const result: InterestResult = {\n type: className,\n data: item,\n name: filter.name,\n };\n\n // Call handler if defined and add to result\n if (filter.handler) {\n result.handled = await filter.handler(item, this);\n }\n\n allResults.push(result);\n }\n }\n\n return allResults;\n }\n\n /**\n * Normalize ObjectInterestConfig to array format\n */\n private normalizeInterestConfig(\n config: ObjectInterestConfig,\n ): InterestFilter[] {\n return Array.isArray(config) ? config : [config];\n }\n\n /**\n * Query a single interest filter\n *\n * Uses collection.query() for custom query functions,\n * or collection.list() for standard SDK filters.\n */\n private async queryInterestFilter(\n _className: string,\n filter: InterestFilter,\n collection: SmrtCollection<SmrtObject>,\n ): Promise<SmrtObject[]> {\n // Custom query path - uses collection.query() for raw SQL power\n if (filter.query) {\n let [whereClause, params] = filter.query(collection.tableName);\n\n // Ensure manifest is loaded for this class and its ancestors (Issue #515)\n // This is critical for cross-package STI where getTableStrategy() needs\n // the complete inheritance chain to detect inherited STI configuration\n //\n // We walk the extends chain directly (not using cached getInheritanceChain)\n // to avoid caching an incomplete chain before all manifests are loaded.\n // After loading all ancestors, we invalidate the cache so getTableStrategy\n // rebuilds it with complete data.\n await ObjectRegistry.ensureManifestLoaded(_className);\n let currentClass = ObjectRegistry.getClass(_className);\n while (currentClass?.extends) {\n const parentName = currentClass.extends;\n // Skip framework base classes\n if (\n parentName === 'SmrtObject' ||\n parentName === 'SmrtClass' ||\n parentName === 'SmrtCollection'\n ) {\n break;\n }\n try {\n await ObjectRegistry.ensureManifestLoaded(parentName);\n } catch {\n // Manifest loading can fail for classes not in manifest - continue\n }\n currentClass = ObjectRegistry.getClass(parentName);\n }\n // Invalidate cached chain so getTableStrategy rebuilds with complete data\n ObjectRegistry.invalidateInheritanceCache(_className);\n\n // Add STI discriminator filter if this is an STI child class.\n // R5-canon: `getSTIBase` returns the qualified name; compare\n // against the qualified form of `_className` so a query against\n // an STI BASE doesn't get an unintended `_meta_type` filter that\n // would hide its descendants.\n const tableStrategy = ObjectRegistry.getTableStrategy(_className);\n if (tableStrategy === 'sti') {\n const stiBase = ObjectRegistry.getSTIBase(_className);\n const classInfo = ObjectRegistry.getClass(_className);\n const qualifiedClassName =\n classInfo?.qualifiedName ?? classInfo?.name ?? _className;\n if (\n stiBase &&\n stiBase !== qualifiedClassName &&\n stiBase !== _className\n ) {\n // Get the qualified name for this class (e.g., '@happyvertical/praeco:Meeting')\n // This is what's stored in the _meta_type column in the database\n const metaTypeValue = classInfo?.qualifiedName || _className;\n // Wrap original where clause and add _meta_type filter\n whereClause = `_meta_type = ? AND (${whereClause})`;\n params = [metaTypeValue, ...params];\n }\n }\n\n // Build full SQL query\n let sql = `SELECT * FROM ${collection.tableName} WHERE ${whereClause}`;\n\n // Add ORDER BY if specified.\n // The sort fields are interpolated directly into the SQL string, so\n // validate each field name and direction against the same allowlist\n // collection.list() uses, to prevent SQL injection if filter.sort ever\n // derives from untrusted input.\n if (filter.sort) {\n const sorts = Array.isArray(filter.sort) ? filter.sort : [filter.sort];\n const orderBy = sorts\n .map((item) => {\n const [field, direction = 'ASC'] = item.trim().split(/\\s+/);\n if (!/^[a-zA-Z0-9_]+$/.test(field)) {\n throw new Error(`Invalid field name for ordering: ${field}`);\n }\n const normalizedDirection = direction.toUpperCase();\n if (\n normalizedDirection !== 'ASC' &&\n normalizedDirection !== 'DESC'\n ) {\n throw new Error(\n `Invalid sort direction: ${direction}. Must be ASC or DESC.`,\n );\n }\n return `${field} ${normalizedDirection}`;\n })\n .join(', ');\n sql += ` ORDER BY ${orderBy}`;\n }\n\n // Add LIMIT if specified\n if (filter.limit) {\n sql += ` LIMIT ?`;\n params.push(filter.limit);\n }\n\n // Execute raw query with hydration\n let items = await collection.query(sql, params);\n\n // Apply qualifier if configured\n if (filter.qualify) {\n items = await filter.qualify(items);\n }\n\n return items;\n }\n\n // Standard filter path - uses collection.list() with SDK filters.\n // Layer the per-instance scope (#1890) as the base so multi-instance agents\n // partition what they process; the global then per-object filters layer on\n // top (winning on key collision). Undefined for singletons → unchanged.\n const mergedFilter = mergeFilters(\n mergeFilters(this.instanceInterestFilter(), this.interests?.filter),\n filter.filter,\n );\n\n const queryOptions: {\n where?: Record<string, unknown>;\n orderBy?: string | string[];\n limit?: number;\n } = {};\n\n if (Object.keys(mergedFilter).length > 0) {\n queryOptions.where = mergedFilter;\n }\n if (filter.sort) {\n queryOptions.orderBy = filter.sort;\n }\n if (filter.limit) {\n queryOptions.limit = filter.limit;\n }\n\n // Execute query\n let items = await collection.list(queryOptions);\n\n // Apply object-specific qualifier if configured\n if (filter.qualify) {\n items = await filter.qualify(items);\n }\n\n return items;\n }\n\n /**\n * Sort results by field(s) across all types\n */\n private sortResults(\n results: InterestResult[],\n sort: string | string[],\n ): InterestResult[] {\n const sortFields = normalizeSort(sort);\n if (sortFields.length === 0) return results;\n\n return [...results].sort((a, b) => {\n for (const sortField of sortFields) {\n const [field, direction = 'ASC'] = sortField.trim().split(/\\s+/);\n const aValue = (a.data as unknown as Record<string, string | number>)[\n field\n ];\n const bValue = (b.data as unknown as Record<string, string | number>)[\n field\n ];\n\n let comparison = 0;\n if (aValue < bValue) comparison = -1;\n else if (aValue > bValue) comparison = 1;\n\n if (comparison !== 0) {\n return direction.toUpperCase() === 'DESC' ? -comparison : comparison;\n }\n }\n return 0;\n });\n }\n}\n\n/**\n * Build the SQL tenant predicate (clause + params) for a raw `_smrt_dispatch`\n * write under the active {@link DispatchTenantScope} (S5 #1398).\n *\n * Mirrors core's `pushTenantPredicate` read/claim semantics so a raw migration\n * UPDATE only ever touches the rows the DispatchBus would let this scope\n * read/claim:\n *\n * - tenancy off (`enforced: false`) → no predicate (pre-tenancy behavior).\n * - active tenant `T` → `(tenant_id = ? OR tenant_id IS NULL)` (own + global).\n * - tenancy on, no active tenant → `tenant_id IS NULL` (fail-closed to global).\n *\n * The returned clause is prefixed with ` AND ` (or empty) so it can be appended\n * directly to an existing `WHERE (...)`.\n */\nfunction buildDispatchTenantUpdatePredicate(\n scope: DispatchTenantScope,\n): [clause: string, params: string[]] {\n if (!scope.enforced) {\n return ['', []];\n }\n if (scope.tenantId !== null) {\n return [' AND (tenant_id = ? OR tenant_id IS NULL)', [scope.tenantId]];\n }\n return [' AND tenant_id IS NULL', []];\n}\n","/**\n * Principal-bound, read-only data-surface tools (#2447).\n *\n * This module deliberately does not know how an application discovers or\n * executes a surface. Applications provide a small, server-side catalog and\n * executor; this package supplies the principal, allow-list, catalog/RBAC,\n * tenant, projection, ordering, and result-boundary enforcement around them.\n */\n\nimport { createHash } from 'node:crypto';\nimport type { AITool } from '@happyvertical/ai';\nimport {\n createDataQueryFingerprint,\n DataQueryValidationError,\n DEFAULT_DATA_QUERY_RESULT_BYTES,\n MAX_DATA_QUERY_FILTERS,\n MAX_DATA_QUERY_REQUEST_BYTES,\n normalizeDataQueryRequest,\n normalizeDataQueryResult,\n normalizeDataQuerySchema,\n type SmrtClassOptions,\n} from '@happyvertical/smrt-core';\nimport type {\n DataQueryFieldDescriptor,\n DataQueryRequest,\n DataQueryResult,\n DataQueryRow,\n DataQuerySchema,\n} from '@happyvertical/smrt-types';\nimport type { PrincipalRun } from './execute-as-principal.js';\nimport type { PrincipalTool, PrincipalToolContext } from './invoke-agent.js';\n\nexport const DATA_DISCOVER_TOOL_SLUG = 'data.discover';\nexport const DATA_INSPECT_TOOL_SLUG = 'data.inspect';\nexport const DATA_QUERY_TOOL_SLUG = 'data.query';\n\nexport const DATA_DISCOVER_FUNCTION_NAME = 'data-discover';\nexport const DATA_INSPECT_FUNCTION_NAME = 'data-inspect';\nexport const DATA_QUERY_FUNCTION_NAME = 'data-query';\n\nexport const DEFAULT_DATA_SURFACE_DEADLINE_MS = 5_000;\nexport const MAX_DATA_SURFACE_DEADLINE_MS = 30_000;\n\n/** Declarative, non-authoritative metadata that a server-owned catalog may expose. */\nexport type DataSurfaceMetadataValue =\n | string\n | number\n | boolean\n | null\n | ReadonlyArray<string | number | boolean | null>;\n\nexport type DataSurfaceFieldMetadata = Readonly<\n Record<string, DataSurfaceMetadataValue>\n>;\n\n/** Surface-level metadata is descriptive only; it never enters query normalization. */\nexport type DataSurfaceMetadata = Readonly<\n Record<string, DataSurfaceMetadataValue>\n>;\n\n/** A data field plus server-owned visibility policy annotations. */\nexport interface DataSurfaceField extends DataQueryFieldDescriptor {\n sensitive?: boolean;\n readPermission?: string;\n metadata?: DataSurfaceFieldMetadata;\n}\n\n/** Server-owned schema; policy annotations never cross the core query boundary. */\nexport interface DataSurfaceSchema extends Omit<DataQuerySchema, 'fields'> {\n fields: DataSurfaceField[];\n}\n\n/** A server-owned data source. Never construct this from model/tool input. */\nexport interface DataSurfaceDefinition {\n /** Stable opaque id presented to the model. */\n id: string;\n /** Permission-catalog collection used for the read gate. */\n collection: string;\n /** Optional backing SMRT class, useful to registry-backed executors. */\n className?: string;\n label?: string;\n description?: string;\n /** Safe catalog metadata, returned only after the read gate succeeds. */\n metadata?: DataSurfaceMetadata;\n schema: DataSurfaceSchema;\n /** Optional surface-specific executor. */\n execute?: DataSurfaceExecutor;\n}\n\nexport interface DataSurfacePrincipal {\n /** The authenticated execution principal, copied from the live run. */\n userId: string;\n /** The authenticated tenant scope, copied from the live run. */\n tenantId: string | null;\n}\n\nexport interface DataSurfaceExecutionContext {\n run: PrincipalRun;\n principal: DataSurfacePrincipal;\n db?: SmrtClassOptions['db'];\n /** Signal for adapters that can cancel database work. */\n signal: AbortSignal;\n}\n\nexport type DataSurfaceExecutorResult =\n | DataQueryResult\n | DataQueryRow[]\n | {\n rows?: DataQueryRow[];\n total?: DataQueryResult['total'];\n facets?: DataQueryResult['facets'];\n freshness?: DataQueryResult['freshness'];\n warnings?: string[];\n truncated?: boolean;\n nextCursor?: string;\n hasMore?: boolean;\n };\n\nexport type DataSurfaceExecutor = (\n surface: DataSurfaceDefinition,\n request: DataQueryRequest,\n context: DataSurfaceExecutionContext,\n) => Promise<DataSurfaceExecutorResult>;\n\nexport interface DataSurfaceAuditEntry {\n action: 'discover' | 'inspect' | 'query';\n surfaceId?: string;\n requestId?: string;\n userId: string;\n tenantId: string | null;\n rowCount?: number;\n truncated?: boolean;\n}\n\nexport type DataSurfaceAuditSink = (\n entry: DataSurfaceAuditEntry,\n) => void | Promise<void>;\n\ntype DataSurfaceAuditInput = Omit<DataSurfaceAuditEntry, 'userId' | 'tenantId'>;\n\nexport interface DataSurfaceToolsOptions {\n /** Server-owned catalog. A function is evaluated per authenticated run. */\n surfaces:\n | readonly DataSurfaceDefinition[]\n | ((\n run: PrincipalRun,\n ) =>\n | readonly DataSurfaceDefinition[]\n | Promise<readonly DataSurfaceDefinition[]>);\n /** Shared executor used when a definition does not provide one. */\n execute?: DataSurfaceExecutor;\n /** Audit sink for individual tool actions. */\n audit?: DataSurfaceAuditSink;\n /** Deadline for an adapter call. Defaults to five seconds. */\n deadlineMs?: number;\n /** Receives detailed server-side failures; never surfaced to the model. */\n onFailure?: DataSurfaceFailureSink;\n}\n\nexport interface DataSurfaceFailureEntry {\n action: 'discover' | 'inspect' | 'query';\n surfaceId?: string;\n requestId?: string;\n userId: string;\n tenantId: string | null;\n error: unknown;\n}\n\nexport type DataSurfaceFailureSink = (\n entry: DataSurfaceFailureEntry,\n) => void | Promise<void>;\n\nexport class DataSurfaceDeniedError extends Error {\n readonly status = 403;\n\n constructor() {\n // Deliberately generic: callers must not learn whether a surface exists.\n super('Data surface is not available.');\n this.name = 'DataSurfaceDeniedError';\n }\n}\n\nexport class DataSurfaceDeadlineError extends Error {\n readonly status = 504;\n\n constructor() {\n super('Data surface query exceeded its execution deadline.');\n this.name = 'DataSurfaceDeadlineError';\n }\n}\n\n/** Adapter output was not in the requested deterministic order. */\nexport class DataSurfaceResultOrderError extends Error {\n readonly status = 502;\n\n constructor() {\n // Do not include field/row values in the public error.\n super('Data surface returned results in an invalid order.');\n this.name = 'DataSurfaceResultOrderError';\n }\n}\n\n/** Stable public failure for executor and result-boundary errors. */\nexport class DataSurfaceQueryError extends Error {\n readonly status = 502;\n readonly code = 'DATA_SURFACE_QUERY_FAILED';\n\n constructor() {\n super('Data surface query failed.');\n this.name = 'DataSurfaceQueryError';\n }\n}\n\n// Audit failures are reported at the point where the audit sink rejects. Keep\n// the wrapped public error marked so the query boundary does not report it a\n// second time when it unwinds through the outer executor catch.\nconst reportedFailureErrors = new WeakSet<object>();\n\n/** Stable public failure for requests that name hidden schema capabilities. */\nexport class DataSurfaceRequestError extends Error {\n readonly status = 400;\n readonly code = 'DATA_SURFACE_REQUEST_INVALID';\n\n constructor() {\n super('Data surface query request is invalid.');\n this.name = 'DataSurfaceRequestError';\n }\n}\n\nconst HIDDEN_SCHEMA_REQUEST_CODES = new Set([\n 'DATA_QUERY_FIELD_NOT_ALLOWED',\n 'DATA_QUERY_PROJECTION_NOT_ALLOWED',\n 'DATA_QUERY_SORT_NOT_ALLOWED',\n 'DATA_QUERY_FACET_NOT_ALLOWED',\n]);\n\nfunction normalizeSurfaceRequest(\n value: unknown,\n schema: DataQuerySchema,\n): DataQueryRequest {\n try {\n return normalizeDataQueryRequest(value, schema);\n } catch (error) {\n if (\n error instanceof DataQueryValidationError &&\n HIDDEN_SCHEMA_REQUEST_CODES.has(error.code)\n ) {\n throw new DataSurfaceRequestError();\n }\n throw error;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isDataQueryRows(value: unknown[]): value is DataQueryRow[] {\n return value.every(isRecord);\n}\n\nfunction dataQueryRowsOrThrow(value: unknown[]): DataQueryRow[] {\n if (!isDataQueryRows(value)) throw new DataSurfaceQueryError();\n return value;\n}\n\nfunction nonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction principalFromRun(run: PrincipalRun): DataSurfacePrincipal {\n const userId = run.context.userId;\n if (!userId) throw new DataSurfaceDeniedError();\n return { userId, tenantId: run.context.tenantId };\n}\n\nfunction coreSchema(schema: DataSurfaceSchema): DataQuerySchema {\n return {\n ...schema,\n fields: schema.fields.map(\n ({\n sensitive: _sensitive,\n readPermission: _readPermission,\n metadata: _metadata,\n ...field\n }) => field,\n ),\n };\n}\n\nfunction visibleSchema(\n schema: DataSurfaceSchema,\n run: PrincipalRun,\n): DataQuerySchema {\n const fields = schema.fields.filter((field) => {\n if (field.sensitive === true) return false;\n const readPermission = field.readPermission;\n if (readPermission && !run.permissions.includes(readPermission)) {\n return false;\n }\n return true;\n });\n if (!fields.some((field) => field.id === schema.identityField)) {\n throw new DataSurfaceDeniedError();\n }\n return normalizeDataQuerySchema(coreSchema({ ...schema, fields }));\n}\n\nfunction descriptor(surface: DataSurfaceDefinition, schema: DataQuerySchema) {\n const metadataByFieldId = new Map(\n surface.schema.fields.map((field) => [field.id, field.metadata]),\n );\n return {\n id: surface.id,\n label: surface.label ?? surface.id,\n ...(surface.description ? { description: surface.description } : {}),\n ...(surface.metadata ? { metadata: surface.metadata } : {}),\n collection: surface.collection,\n identityField: schema.identityField,\n fields: schema.fields.map((field) => ({\n id: field.id,\n type: field.type,\n projectable: field.projectable !== false,\n sortable: field.sortable === true,\n facetable: field.facetable === true,\n filterOperators: [...(field.filterOperators ?? [])].sort(),\n ...(metadataByFieldId.get(field.id)\n ? { metadata: metadataByFieldId.get(field.id) }\n : {}),\n })),\n supports: schema.supports ?? {},\n limits: {\n defaultPageLimit: schema.defaultPageLimit,\n maxPageLimit: schema.maxPageLimit,\n maxResultBytes: schema.maxResultBytes,\n },\n };\n}\n\nasync function availableSurfaces(\n options: DataSurfaceToolsOptions,\n run: PrincipalRun,\n): Promise<Array<{ surface: DataSurfaceDefinition; schema: DataQuerySchema }>> {\n const configured =\n typeof options.surfaces === 'function'\n ? await options.surfaces(run)\n : options.surfaces;\n const result: Array<{\n surface: DataSurfaceDefinition;\n schema: DataQuerySchema;\n }> = [];\n for (const surface of configured) {\n if (\n !surface ||\n !nonEmptyString(surface.id) ||\n !nonEmptyString(surface.collection)\n )\n continue;\n try {\n // A missing catalog permission is intentionally indistinguishable from a\n // missing surface. The allow-list gate is checked before this function.\n await run.assertOperation(surface.collection, 'read');\n result.push({ surface, schema: visibleSchema(surface.schema, run) });\n } catch {\n // Do not leak unauthorized surface ids, schemas, or permission errors.\n }\n }\n return result.sort((left, right) =>\n left.surface.id === right.surface.id\n ? 0\n : left.surface.id < right.surface.id\n ? -1\n : 1,\n );\n}\n\nfunction findSurface(\n surfaces: Array<{ surface: DataSurfaceDefinition; schema: DataQuerySchema }>,\n id: unknown,\n) {\n return surfaces.find((entry) => entry.surface.id === id);\n}\n\nfunction sortRows(\n rows: DataQueryRow[],\n request: DataQueryRequest,\n schema: DataQuerySchema,\n): DataQueryRow[] {\n const terms = request.sort ?? [];\n return [...rows].sort((left, right) =>\n compareRows(left, right, terms, schema),\n );\n}\n\nfunction compareDataValues(\n left: unknown,\n right: unknown,\n type: DataQuerySchema['fields'][number]['type'],\n): number {\n if (left === right) return 0;\n if (left === null || left === undefined) return -1;\n if (right === null || right === undefined) return 1;\n if (type === 'number') return Number(left) - Number(right);\n if (type === 'datetime') {\n const leftTime = Date.parse(String(left));\n const rightTime = Date.parse(String(right));\n if (Number.isFinite(leftTime) && Number.isFinite(rightTime)) {\n return leftTime - rightTime;\n }\n }\n if (type === 'boolean') return Number(Boolean(left)) - Number(Boolean(right));\n const leftString = String(left);\n const rightString = String(right);\n return leftString === rightString ? 0 : leftString < rightString ? -1 : 1;\n}\n\nfunction compareRows(\n left: DataQueryRow,\n right: DataQueryRow,\n terms: readonly NonNullable<DataQueryRequest['sort']>[number][],\n schema: DataQuerySchema,\n): number {\n for (const term of terms) {\n const type =\n schema.fields.find((field) => field.id === term.field)?.type ?? 'string';\n const result = compareDataValues(left[term.field], right[term.field], type);\n if (result !== 0) return term.direction === 'desc' ? -result : result;\n }\n const identityType =\n schema.fields.find((field) => field.id === schema.identityField)?.type ??\n 'string';\n return compareDataValues(\n left[schema.identityField],\n right[schema.identityField],\n identityType,\n );\n}\n\nfunction isCanonicalOrder(\n rows: DataQueryRow[],\n request: DataQueryRequest,\n schema: DataQuerySchema,\n): boolean {\n const terms = request.sort ?? [];\n for (let index = 1; index < rows.length; index += 1) {\n if (compareRows(rows[index - 1], rows[index], terms, schema) > 0) {\n return false;\n }\n }\n return true;\n}\n\nfunction projectionForResult(request: DataQueryRequest): string[] {\n return request.projection ?? [];\n}\n\nfunction externalValidationRequest(\n request: DataQueryRequest,\n schema: DataQuerySchema,\n): DataQueryRequest {\n if (\n request.mode !== 'rows' ||\n !request.projection ||\n request.projection.length <= MAX_DATA_QUERY_FILTERS\n ) {\n return request;\n }\n return {\n ...request,\n projection: request.projection.filter(\n (field) => field !== schema.identityField,\n ),\n };\n}\n\nfunction canonicalRequestValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(canonicalRequestValue);\n if (isRecord(value)) {\n return Object.fromEntries(\n Object.keys(value)\n .sort()\n .map((key) => [key, canonicalRequestValue(value[key])]),\n );\n }\n return value;\n}\n\n/**\n * Create the fingerprint for the already-normalized request passed to a\n * surface executor. This supports internal projections beyond core's public\n * 50-field projection limit; callers must use the exact request received.\n */\nexport function createDataSurfaceQueryFingerprint(\n request: DataQueryRequest,\n): string {\n const { requestId: _requestId, page: _page, ...semanticQuery } = request;\n return `dq1_${createHash('sha256')\n .update(JSON.stringify(canonicalRequestValue(semanticQuery)))\n .digest('base64url')}`;\n}\n\nfunction shorthandResultCandidate(\n request: DataQueryRequest,\n schema: DataQuerySchema,\n rawRecord: Record<string, unknown> | undefined,\n rows: readonly unknown[],\n): Record<string, unknown> {\n const rawPage = isRecord(rawRecord?.page) ? rawRecord.page : undefined;\n const explicitHasMore =\n typeof rawPage?.hasMore === 'boolean'\n ? rawPage.hasMore\n : typeof rawRecord?.hasMore === 'boolean'\n ? rawRecord.hasMore\n : undefined;\n const nextCursor =\n typeof rawPage?.nextCursor === 'string'\n ? rawPage.nextCursor\n : typeof rawRecord?.nextCursor === 'string'\n ? rawRecord.nextCursor\n : undefined;\n if (\n request.page &&\n rows.length === request.page.limit &&\n explicitHasMore === undefined &&\n !nextCursor\n ) {\n // An exact-limit shorthand page may have more rows. Require the adapter\n // to provide continuation metadata instead of falsely declaring a final\n // page and silently truncating a result set.\n throw new DataSurfaceQueryError();\n }\n return {\n version: 1,\n requestId: request.requestId,\n queryFingerprint: createDataQueryFingerprint(request, schema),\n identityField: schema.identityField,\n rows,\n ...(request.page\n ? {\n page:\n request.page.kind === 'offset'\n ? {\n kind: 'offset',\n offset: request.page.offset,\n limit: request.page.limit,\n hasMore: explicitHasMore ?? Boolean(nextCursor),\n }\n : {\n kind: 'cursor',\n limit: request.page.limit,\n hasMore: explicitHasMore ?? Boolean(nextCursor),\n ...(nextCursor ? { nextCursor } : {}),\n },\n }\n : {}),\n total: rawRecord?.total ?? { kind: 'unavailable' },\n ...(rawRecord?.facets ? { facets: rawRecord.facets } : {}),\n freshness: rawRecord?.freshness ?? { state: 'unknown' },\n warnings: Array.isArray(rawRecord?.warnings) ? rawRecord.warnings : [],\n truncated: rawRecord?.truncated === true,\n };\n}\n\nfunction normalizeWideRows(\n rawRecord: Record<string, unknown> | undefined,\n rawRows: unknown[],\n request: DataQueryRequest,\n resultRequest: DataQueryRequest,\n schema: DataQuerySchema,\n internal: { request: DataQueryRequest; schema: DataQuerySchema },\n): DataQueryResult {\n const hasVersionedResult =\n rawRecord !== undefined && Object.hasOwn(rawRecord, 'version');\n if (\n hasVersionedResult &&\n (rawRecord.version !== 1 ||\n rawRecord.requestId !== internal.request.requestId ||\n rawRecord.identityField !== internal.schema.identityField ||\n rawRecord.queryFingerprint !==\n createDataSurfaceQueryFingerprint(internal.request))\n ) {\n throw new DataSurfaceQueryError();\n }\n const requestedFields = resultRequest.projection ?? [schema.identityField];\n const sortOnlyFields = new Set(\n (request.sort ?? [])\n .map((term) => term.field)\n .filter((field) => !requestedFields.includes(field)),\n );\n const chunkSize = MAX_DATA_QUERY_FILTERS - 1;\n const chunks: DataQueryResult[] = [];\n for (let offset = 0; offset < requestedFields.length; offset += chunkSize) {\n const fields = requestedFields.slice(offset, offset + chunkSize);\n const allowedFields = new Set([\n schema.identityField,\n ...requestedFields,\n ...sortOnlyFields,\n ]);\n const chunkFields = new Set([schema.identityField, ...fields]);\n const chunkRows = rawRows.map((row) => {\n if (!isRecord(row)) return row;\n if (Object.keys(row).some((field) => !allowedFields.has(field))) {\n return row;\n }\n return Object.fromEntries(\n Object.entries(row).filter(([field]) => chunkFields.has(field)),\n );\n });\n // Chunk validation checks field values and page bounds; ordering is\n // validated separately against the complete internal request below.\n const chunkRequest = { ...resultRequest, projection: fields, sort: [] };\n // Correlation fields on a versioned result are checked above before this\n // per-chunk validation envelope is constructed. The chunk fingerprint\n // is necessarily different from the full internal projection's\n // fingerprint because core's normalizer has a 50-field projection cap.\n const candidate = hasVersionedResult\n ? {\n ...rawRecord,\n requestId: chunkRequest.requestId,\n queryFingerprint: createDataQueryFingerprint(chunkRequest, schema),\n identityField: schema.identityField,\n rows: chunkRows,\n }\n : shorthandResultCandidate(chunkRequest, schema, rawRecord, chunkRows);\n chunks.push(normalizeDataQueryResult(candidate, chunkRequest, schema));\n }\n if (chunks.length === 0) {\n throw new DataSurfaceQueryError();\n }\n const rows = chunks[0].rows.map((_, index) =>\n Object.assign({}, ...chunks.map((chunk) => chunk.rows[index])),\n );\n const result = {\n ...chunks[0],\n requestId: request.requestId,\n queryFingerprint: createDataSurfaceQueryFingerprint(request),\n identityField: schema.identityField,\n rows,\n };\n const bytes = new TextEncoder().encode(JSON.stringify(result)).byteLength;\n if (bytes > (schema.maxResultBytes ?? DEFAULT_DATA_QUERY_RESULT_BYTES)) {\n throw new DataSurfaceQueryError();\n }\n return result;\n}\n\nfunction addSortOnlyValues(\n rows: DataQueryRow[],\n rawRows: DataQueryRow[],\n request: DataQueryRequest,\n internalSchema: DataQuerySchema,\n rawRecord: Record<string, unknown> | undefined,\n): DataQueryRow[] {\n const projection = new Set(\n request.projection ?? [internalSchema.identityField],\n );\n const sortOnly = (request.sort ?? [])\n .map((term) => term.field)\n .filter((field) => !projection.has(field));\n if (sortOnly.length === 0) return rows;\n const chunkSize = MAX_DATA_QUERY_FILTERS - 1;\n const validatedChunks: DataQueryResult[] = [];\n for (let offset = 0; offset < sortOnly.length; offset += chunkSize) {\n const fields = sortOnly.slice(offset, offset + chunkSize);\n const validationProjection = [\n ...new Set([internalSchema.identityField, ...fields]),\n ].sort();\n const validationRequest = {\n ...request,\n projection: validationProjection,\n sort: [],\n };\n const validationRows = rawRows.map((row) =>\n Object.fromEntries(\n Object.entries(row).filter(([field]) =>\n validationProjection.includes(field),\n ),\n ),\n );\n validatedChunks.push(\n normalizeDataQueryResult(\n shorthandResultCandidate(\n validationRequest,\n internalSchema,\n rawRecord,\n validationRows,\n ),\n validationRequest,\n internalSchema,\n ),\n );\n }\n return rows.map((row, index) => {\n const result = { ...row };\n for (const field of sortOnly) {\n for (const chunk of validatedChunks) {\n const validatedRow = chunk.rows[index];\n if (Object.hasOwn(validatedRow, field)) {\n result[field] = validatedRow[field];\n break;\n }\n }\n }\n return result;\n });\n}\n\nfunction buildInternalQuery(\n request: DataQueryRequest,\n schema: DataQuerySchema,\n): { request: DataQueryRequest; schema: DataQuerySchema } {\n const sort = request.sort ?? [];\n if (request.mode !== 'rows' || sort.length === 0) {\n return { request, schema };\n }\n const sortFields = new Set(sort.map((term) => term.field));\n const internalSchema = {\n ...schema,\n fields: schema.fields.map((field) =>\n sortFields.has(field.id) ? { ...field, projectable: true } : field,\n ),\n };\n const projection = [\n ...new Set([\n ...(request.projection ?? [schema.identityField]),\n ...sortFields,\n ]),\n ].sort();\n const internalRequest = { ...request, projection };\n const requestBytes = new TextEncoder().encode(\n JSON.stringify(internalRequest),\n ).byteLength;\n if (requestBytes > MAX_DATA_QUERY_REQUEST_BYTES) {\n throw new DataSurfaceQueryError();\n }\n return {\n schema: internalSchema,\n request: internalRequest,\n };\n}\n\nfunction requireSortValues(\n rows: DataQueryRow[],\n request: DataQueryRequest,\n): void {\n for (const row of rows) {\n for (const term of request.sort ?? []) {\n if (!Object.hasOwn(row, term.field)) {\n throw new DataSurfaceResultOrderError();\n }\n }\n }\n}\n\nfunction stripInternalProjection(\n rows: DataQueryRow[],\n request: DataQueryRequest,\n): DataQueryRow[] {\n const projection = projectionForResult(request).filter(Boolean);\n return rows.map((row) =>\n Object.fromEntries(\n projection\n .filter((field) => Object.hasOwn(row, field))\n .map((field) => [field, row[field]]),\n ),\n );\n}\n\nasync function reportFailure(\n options: DataSurfaceToolsOptions,\n run: PrincipalRun,\n action: DataSurfaceFailureEntry['action'],\n surfaceId: string | undefined,\n requestId: string | undefined,\n error: unknown,\n): Promise<void> {\n try {\n const principal = principalFromRun(run);\n await options.onFailure?.({\n action,\n ...(surfaceId !== undefined ? { surfaceId } : {}),\n requestId,\n ...principal,\n error,\n });\n } catch {\n // Failure telemetry must never alter the stable public error contract.\n }\n}\n\nasync function bounded<T>(\n promise: Promise<T>,\n deadlineMs: number,\n controller: AbortController,\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n // Adapters may observe this signal and cancel their database request.\n controller.abort();\n reject(new DataSurfaceDeadlineError());\n }, deadlineMs);\n });\n const abort = new Promise<never>((_, reject) => {\n controller.signal.addEventListener(\n 'abort',\n () => reject(new DataSurfaceDeadlineError()),\n { once: true },\n );\n });\n try {\n return await Promise.race([promise, timeout, abort]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nfunction requestFromArgs(args: Record<string, unknown>): unknown {\n return args.request ?? args;\n}\n\nfunction tool(\n slug: string,\n functionName: string,\n description: string,\n parameters: Record<string, unknown>,\n execute: (context: PrincipalToolContext) => Promise<unknown>,\n): PrincipalTool {\n const aiTool: AITool = {\n type: 'function',\n function: { name: functionName, description, parameters },\n };\n return { slug, aiTool, execute };\n}\n\n/** Build the discover/inspect/query tools for a persona conversation. */\nexport function createDataSurfaceTools(\n options: DataSurfaceToolsOptions,\n): PrincipalTool[] {\n const deadlineMs = Math.min(\n Math.max(options.deadlineMs ?? DEFAULT_DATA_SURFACE_DEADLINE_MS, 1),\n MAX_DATA_SURFACE_DEADLINE_MS,\n );\n const audit = async (\n entry: DataSurfaceAuditInput,\n run: PrincipalRun,\n ): Promise<void> => {\n try {\n const principal = principalFromRun(run);\n await options.audit?.({ ...entry, ...principal });\n } catch (error) {\n await reportFailure(\n options,\n run,\n entry.action,\n entry.surfaceId,\n entry.requestId,\n error,\n );\n const publicError = new DataSurfaceQueryError();\n reportedFailureErrors.add(publicError);\n throw publicError;\n }\n };\n const catalog = async (\n run: PrincipalRun,\n action: DataSurfaceFailureEntry['action'],\n ) => {\n try {\n return await availableSurfaces(options, run);\n } catch (error) {\n await reportFailure(options, run, action, undefined, undefined, error);\n throw new DataSurfaceQueryError();\n }\n };\n\n const discover = tool(\n DATA_DISCOVER_TOOL_SLUG,\n DATA_DISCOVER_FUNCTION_NAME,\n 'List data surfaces and their safe, readable fields.',\n { type: 'object', properties: {}, additionalProperties: false },\n async ({ run }) => {\n run.assertToolAllowed(DATA_DISCOVER_TOOL_SLUG);\n const entries = await catalog(run, 'discover');\n await audit({ action: 'discover' }, run);\n return entries.map(({ surface, schema }) => descriptor(surface, schema));\n },\n );\n\n const inspect = tool(\n DATA_INSPECT_TOOL_SLUG,\n DATA_INSPECT_FUNCTION_NAME,\n 'Inspect one readable data surface schema.',\n {\n type: 'object',\n required: ['surfaceId'],\n properties: { surfaceId: { type: 'string' } },\n additionalProperties: false,\n },\n async ({ run, args }) => {\n run.assertToolAllowed(DATA_INSPECT_TOOL_SLUG);\n const entry = findSurface(await catalog(run, 'inspect'), args.surfaceId);\n if (!entry) throw new DataSurfaceDeniedError();\n await audit({ action: 'inspect', surfaceId: entry.surface.id }, run);\n return descriptor(entry.surface, entry.schema);\n },\n );\n\n const query = tool(\n DATA_QUERY_TOOL_SLUG,\n DATA_QUERY_FUNCTION_NAME,\n 'Run a bounded read query against one readable data surface.',\n {\n type: 'object',\n required: ['surfaceId', 'request'],\n properties: {\n surfaceId: { type: 'string' },\n request: { type: 'object' },\n },\n additionalProperties: false,\n },\n async ({ run, args, db }) => {\n run.assertToolAllowed(DATA_QUERY_TOOL_SLUG);\n const entry = findSurface(await catalog(run, 'query'), args.surfaceId);\n if (!entry) throw new DataSurfaceDeniedError();\n const request = normalizeSurfaceRequest(\n requestFromArgs(args),\n entry.schema,\n );\n const principal = principalFromRun(run);\n const signal = new AbortController();\n const executor = entry.surface.execute ?? options.execute;\n if (!executor) throw new DataSurfaceDeniedError();\n try {\n const internal = buildInternalQuery(request, entry.schema);\n const raw = await bounded(\n executor(entry.surface, internal.request, {\n run,\n principal,\n db: run.context.database ?? db,\n signal: signal.signal,\n }),\n deadlineMs,\n signal,\n );\n const rawRecord = isRecord(raw) ? raw : undefined;\n const rawRows = Array.isArray(raw)\n ? raw\n : rawRecord && Array.isArray(rawRecord.rows)\n ? rawRecord.rows\n : [];\n if (\n request.mode === 'rows' &&\n request.page &&\n rawRows.length > request.page.limit\n ) {\n throw new DataSurfaceQueryError();\n }\n const resultRequest = externalValidationRequest(request, entry.schema);\n const hasVersionedResult =\n rawRecord && Object.hasOwn(rawRecord, 'version');\n const canValidateInternal =\n (internal.request.projection?.length ?? 0) <= MAX_DATA_QUERY_FILTERS;\n const validated = canValidateInternal\n ? normalizeDataQueryResult(\n hasVersionedResult\n ? raw\n : shorthandResultCandidate(\n internal.request,\n internal.schema,\n rawRecord,\n rawRows,\n ),\n internal.request,\n internal.schema,\n )\n : normalizeWideRows(\n rawRecord,\n rawRows,\n request,\n resultRequest,\n entry.schema,\n internal,\n );\n const rawOrderRows =\n request.mode === 'rows' && !canValidateInternal\n ? dataQueryRowsOrThrow(rawRows)\n : validated.rows;\n const orderRows =\n request.mode === 'rows' && !canValidateInternal\n ? addSortOnlyValues(\n validated.rows,\n rawOrderRows,\n request,\n internal.schema,\n rawRecord,\n )\n : rawOrderRows;\n if (request.mode === 'rows') {\n requireSortValues(orderRows, internal.request);\n }\n const orderedRows =\n request.mode === 'rows' && request.page === undefined\n ? sortRows(orderRows, internal.request, internal.schema)\n : orderRows;\n if (\n request.mode === 'rows' &&\n request.page !== undefined &&\n !isCanonicalOrder(orderedRows, internal.request, internal.schema)\n ) {\n throw new DataSurfaceResultOrderError();\n }\n const resultCandidate = {\n ...validated,\n requestId: resultRequest.requestId,\n queryFingerprint: canValidateInternal\n ? createDataQueryFingerprint(resultRequest, entry.schema)\n : createDataSurfaceQueryFingerprint(request),\n identityField: entry.schema.identityField,\n rows: stripInternalProjection(orderedRows, request),\n };\n const result: DataQueryResult = canValidateInternal\n ? normalizeDataQueryResult(\n resultCandidate,\n resultRequest,\n entry.schema,\n )\n : {\n ...resultCandidate,\n queryFingerprint: createDataSurfaceQueryFingerprint(request),\n };\n await audit(\n {\n action: 'query',\n surfaceId: entry.surface.id,\n requestId: result.requestId,\n rowCount: result.rows.length,\n truncated: result.truncated,\n },\n run,\n );\n return result;\n } catch (error) {\n const alreadyReported =\n (typeof error === 'object' && error !== null) ||\n typeof error === 'function'\n ? reportedFailureErrors.has(error)\n : false;\n if (!alreadyReported) {\n await reportFailure(\n options,\n run,\n 'query',\n entry.surface.id,\n request.requestId,\n error,\n );\n }\n if (\n error instanceof DataSurfaceDeadlineError ||\n error instanceof DataSurfaceResultOrderError ||\n error instanceof DataSurfaceQueryError\n ) {\n throw error;\n }\n throw new DataSurfaceQueryError();\n }\n },\n );\n\n return [discover, inspect, query];\n}\n","/**\n * Delegation envelope — the immutable principal + bounded depth carried along an\n * agent-orchestration chain (L3 of the learning-agents epic, #1892).\n *\n * When a conversational (orchestrator) agent invokes a worker agent, and that\n * worker in turn invokes a further worker, the whole chain must run as **one**\n * principal — the originating user — and can never widen it. This module is the\n * pure value object that encodes that invariant:\n *\n * - **Principal immutability.** `runAsUserId`, `tenantId`, and the originating\n * `onBehalfOfUserId` are copied verbatim from parent to child by\n * {@link deriveDelegationEnvelope}; there is no parameter to change them. A\n * caller that *requests* a different principal (e.g. a compromised worker\n * passing `runAsUserId` through the invoke-agent tool) is rejected by\n * {@link assertPrincipalNotWidened} — the request is honoured only when it\n * exactly equals the parent principal.\n * - **Bounded depth.** Every derivation increments `depth` and asserts it stays\n * within {@link MAX_DELEGATION_DEPTH}, so an orchestration chain (or an\n * accidental invoke-yourself loop) can never recurse without limit.\n *\n * The envelope carries no authority of its own: the actual permission bound is\n * still the originating user's live RBAC, enforced when the worker runs via\n * `executeAsPrincipal` (Postgres RLS, or the catalog assert on RLS-off\n * adapters). The envelope only guarantees *which* principal that is and *how\n * deep* the chain may go.\n *\n * @module\n */\n\n/**\n * Maximum delegation depth for an orchestration chain. The orchestrator's own\n * conversation is depth `0`; the first worker it invokes is depth `1`. A worker\n * may invoke a further worker only while the resulting child depth stays within\n * this ceiling, so a chain is at most `MAX_DELEGATION_DEPTH` workers long.\n */\nexport const MAX_DELEGATION_DEPTH = 3;\n\n/**\n * The immutable principal + bounded depth carried from an orchestrator to a\n * worker (and along any further delegation). Serializable, so it can travel in a\n * job's args or a DispatchBus payload to a worker running out of process.\n */\nexport interface DelegationEnvelope {\n /**\n * The user whose live permissions bound the worker's execution. Immutable\n * along the chain — copied verbatim from parent to child.\n */\n runAsUserId: string;\n /** Tenant the principal acts within. Immutable along the chain. */\n tenantId: string | null;\n /**\n * The originating user the whole chain acts **on behalf of** (audited). This\n * is the human who started the conversation; it never changes as delegation\n * deepens, so every action along the chain audits back to the same person.\n */\n onBehalfOfUserId: string;\n /**\n * Current delegation depth. `0` for the orchestrator, `1` for its first\n * worker, and so on — bounded by {@link MAX_DELEGATION_DEPTH}.\n */\n depth: number;\n /**\n * Correlation id linking a worker invocation to the completion dispatch it\n * emits, so the orchestrator can surface the result back into the\n * conversation.\n */\n correlationId: string;\n /**\n * The worker's tool ceiling (its persona's `allowedTools`), carried so a\n * worker that itself runs a tool loop is bounded fail-closed. `undefined`\n * normalizes to \"no tools\" at `executeAsPrincipal` — it never widens.\n */\n allowedTools?: string[];\n}\n\n/**\n * The principal fields a caller may *request* when deriving a child envelope.\n * Any field that is provided must equal the parent's, or\n * {@link assertPrincipalNotWidened} throws — the principal can only ever be\n * inherited, never changed.\n */\nexport interface RequestedPrincipal {\n runAsUserId?: string;\n tenantId?: string | null;\n onBehalfOfUserId?: string;\n}\n\n/**\n * Thrown when a delegation would exceed {@link MAX_DELEGATION_DEPTH}.\n */\nexport class DelegationDepthExceededError extends Error {\n readonly depth: number;\n readonly maxDepth: number;\n readonly status = 400;\n\n constructor(depth: number, maxDepth: number) {\n super(\n `Delegation depth ${depth} exceeds the maximum of ${maxDepth}; ` +\n 'a worker cannot invoke a further worker beyond this depth.',\n );\n this.name = 'DelegationDepthExceededError';\n this.depth = depth;\n this.maxDepth = maxDepth;\n }\n}\n\n/**\n * Thrown when a delegation would *widen* the principal — i.e. a caller requests\n * a `runAsUserId` / `tenantId` / `onBehalfOfUserId` that differs from the\n * parent's. The principal is immutable along an orchestration chain.\n */\nexport class PrincipalWideningError extends Error {\n readonly field: keyof RequestedPrincipal;\n readonly status = 403;\n\n constructor(\n field: keyof RequestedPrincipal,\n expected: unknown,\n got: unknown,\n ) {\n super(\n `Delegation cannot widen the principal: '${String(field)}' is immutable ` +\n `along the chain (bound to ${JSON.stringify(expected)}, ` +\n `refusing ${JSON.stringify(got)}).`,\n );\n this.name = 'PrincipalWideningError';\n this.field = field;\n }\n}\n\n/**\n * Assert a delegation depth is a valid, in-bounds depth.\n *\n * Rejects a non-integer, negative, or non-finite depth as well as one past the\n * ceiling. This matters for the untrusted-payload path: an envelope\n * reconstructed from a persisted dispatch/job could carry `NaN`, a negative, or\n * a string-coerced value, and `NaN > maxDepth` is `false` — so a bare\n * upper-bound check would let it silently bypass the bound and make delegation\n * effectively unbounded.\n *\n * @throws {@link DelegationDepthExceededError} when `depth` is not an integer in `[0, maxDepth]`.\n */\nexport function assertWithinDelegationDepth(\n depth: number,\n maxDepth: number = MAX_DELEGATION_DEPTH,\n): void {\n if (!Number.isInteger(depth) || depth < 0 || depth > maxDepth) {\n throw new DelegationDepthExceededError(depth, maxDepth);\n }\n}\n\n/**\n * Assert a *requested* principal does not widen the parent's.\n *\n * Each provided field must exactly equal the parent's; a mismatch throws\n * {@link PrincipalWideningError}. Omitted fields are fine — they inherit. This\n * is the defence-in-depth guard for the case where an envelope is reconstructed\n * from an untrusted source (a worker's invoke-agent arguments, a job payload):\n * the principal is only ever accepted when it matches, so it can never expand.\n */\nexport function assertPrincipalNotWidened(\n parent: Pick<\n DelegationEnvelope,\n 'runAsUserId' | 'tenantId' | 'onBehalfOfUserId'\n >,\n requested: RequestedPrincipal,\n): void {\n if (\n requested.runAsUserId !== undefined &&\n requested.runAsUserId !== parent.runAsUserId\n ) {\n throw new PrincipalWideningError(\n 'runAsUserId',\n parent.runAsUserId,\n requested.runAsUserId,\n );\n }\n if (\n requested.tenantId !== undefined &&\n requested.tenantId !== parent.tenantId\n ) {\n throw new PrincipalWideningError(\n 'tenantId',\n parent.tenantId,\n requested.tenantId,\n );\n }\n if (\n requested.onBehalfOfUserId !== undefined &&\n requested.onBehalfOfUserId !== parent.onBehalfOfUserId\n ) {\n throw new PrincipalWideningError(\n 'onBehalfOfUserId',\n parent.onBehalfOfUserId,\n requested.onBehalfOfUserId,\n );\n }\n}\n\n/**\n * Options for {@link rootDelegationEnvelope}.\n */\nexport interface RootDelegationEnvelopeOptions {\n /** The principal the orchestrator (and thus the whole chain) runs as. */\n runAsUserId: string;\n /** Tenant the principal acts within. */\n tenantId: string | null;\n /**\n * The originating user the chain acts on behalf of. Defaults to\n * `runAsUserId` when the orchestrator is itself operating directly.\n */\n onBehalfOfUserId?: string;\n /** Correlation id. A fresh UUID is generated when omitted. */\n correlationId?: string;\n /**\n * The orchestrator's own tool ceiling. Carried for completeness; workers do\n * **not** inherit it — a worker's ceiling comes from trusted per-worker policy\n * (`resolveWorkerAllowedTools`) and is fail-closed (no tools) when absent.\n */\n allowedTools?: string[];\n}\n\n/**\n * Build the depth-`0` (orchestrator) envelope that seeds an orchestration chain.\n *\n * The orchestrator's conversation is depth `0`; {@link deriveDelegationEnvelope}\n * produces the depth-`1` envelope for the first worker it invokes.\n */\nexport function rootDelegationEnvelope(\n options: RootDelegationEnvelopeOptions,\n): DelegationEnvelope {\n return {\n runAsUserId: options.runAsUserId,\n tenantId: options.tenantId,\n onBehalfOfUserId: options.onBehalfOfUserId ?? options.runAsUserId,\n depth: 0,\n correlationId: options.correlationId ?? crypto.randomUUID(),\n allowedTools: options.allowedTools,\n };\n}\n\n/**\n * Options for {@link deriveDelegationEnvelope}.\n */\nexport interface DeriveDelegationEnvelopeOptions {\n /** Correlation id for the child invocation. A fresh UUID when omitted. */\n correlationId?: string;\n /**\n * The invoked worker's tool ceiling. When omitted the child carries no tools\n * (fail-closed); it is **not** inherited from the parent so a worker never\n * silently gains the orchestrator's tools.\n */\n allowedTools?: string[];\n /**\n * A principal a caller is *requesting* the child run as. Accepted only when it\n * matches the parent principal exactly (see {@link assertPrincipalNotWidened});\n * otherwise {@link PrincipalWideningError} is thrown. Omit to inherit.\n */\n requestedPrincipal?: RequestedPrincipal;\n /** Depth ceiling override (mainly for tests). */\n maxDepth?: number;\n}\n\n/**\n * Derive the child envelope for a worker invoked by the holder of `parent`.\n *\n * The child **inherits the parent's principal verbatim** (`runAsUserId`,\n * `tenantId`, `onBehalfOfUserId`) — there is no way to change it — increments\n * the depth (asserting the ceiling), and carries the invoked worker's own tool\n * ceiling. A `requestedPrincipal` that differs from the parent's is rejected, so\n * a worker can never invoke a further worker under a broader principal.\n *\n * @throws {@link DelegationDepthExceededError} when the child would exceed the depth ceiling.\n * @throws {@link PrincipalWideningError} when a requested principal widens the parent's.\n */\nexport function deriveDelegationEnvelope(\n parent: DelegationEnvelope,\n options: DeriveDelegationEnvelopeOptions = {},\n): DelegationEnvelope {\n const depth = parent.depth + 1;\n assertWithinDelegationDepth(depth, options.maxDepth);\n if (options.requestedPrincipal) {\n assertPrincipalNotWidened(parent, options.requestedPrincipal);\n }\n return {\n // Principal is copied verbatim — immutable along the chain.\n runAsUserId: parent.runAsUserId,\n tenantId: parent.tenantId,\n onBehalfOfUserId: parent.onBehalfOfUserId,\n depth,\n correlationId: options.correlationId ?? crypto.randomUUID(),\n allowedTools: options.allowedTools,\n };\n}\n","/**\n * invoke-agent — agent orchestration via principal delegation (L3 of the\n * learning-agents epic, #1892).\n *\n * A conversational (orchestrator) agent invokes a worker agent through a\n * standard **`invoke-agent` tool** — gated by the persona's `allowedTools` like\n * any other tool. The tool hands the work to a worker **under the orchestrator's\n * own principal** (`runAsUserId` + `tenantId`), the worker runs via\n * {@link executeAsPrincipal} under that same principal, and reports completion\n * back into the conversation via a **correlated completion dispatch**.\n *\n * This is deliberately **not a new engine**. It is the `invoke-agent` tool plus\n * a completion-dispatch convention on top of the machinery that already ships:\n *\n * - {@link executeAsPrincipal} runs the worker as the delegated principal, so\n * the worker's authority is the originating user's live RBAC — never the\n * worker's own — and every action audits on-behalf-of that user.\n * - The {@link DelegationEnvelope} makes the principal **immutable along the\n * chain** (a worker cannot invoke a further worker under a broader principal)\n * and **bounds delegation depth**.\n * - The DispatchBus carries both the (optional) async invoke signal and the\n * correlated completion, so a worker's result can be surfaced back into the\n * conversation.\n *\n * The **transport is pluggable**. The default {@link inlineInvokeAgentTransport}\n * runs the worker in-process and returns the completion as the tool observation\n * (so it surfaces in the same turn). {@link createDispatchInvokeTransport} emits\n * a DispatchBus `agent.invoke` signal a worker processes out of band\n * ({@link processAgentInvocations}); a job-queue transport (enqueue on the\n * `agents` queue) is a consumer-supplied `InvokeAgentTransport` — orchestration\n * never hard-depends on `@happyvertical/smrt-jobs`, which sits *below* agents in\n * the dependency graph.\n *\n * @module\n */\n\nimport type { AITool } from '@happyvertical/ai';\nimport { createLogger, type Logger } from '@happyvertical/logger';\nimport type { DispatchBus, SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n assertWithinDelegationDepth,\n type DelegationEnvelope,\n deriveDelegationEnvelope,\n} from './delegation.js';\nimport {\n executeAsPrincipal,\n type PrincipalAuditSink,\n type PrincipalRun,\n} from './execute-as-principal.js';\n\n/** Catalog slug + permission id of the standard invoke-agent tool. */\nexport const INVOKE_AGENT_TOOL_SLUG = 'agents.invoke';\n\n/**\n * Provider-safe function name the model receives for the invoke-agent tool.\n * Catalog slugs contain a `.` which some providers (OpenAI) reject in function\n * names; the loop resolves a call by either the slug or this name.\n */\nexport const INVOKE_AGENT_FUNCTION_NAME = 'agents-invoke';\n\n/** DispatchBus signal prefix a worker is invoked through in the async transport. */\nexport const AGENT_INVOKE_SIGNAL = 'agent.invoke';\n\n/** DispatchBus signal a worker emits to report completion, correlated by id. */\nexport const AGENT_COMPLETED_SIGNAL = 'agent.completed';\n\n/**\n * The **per-worker** signal type an async invocation is emitted on, so a\n * processor only ever claims invocations for the worker class it serves.\n *\n * The async transport emits `agent.invoke.<agentClass>` (the class rendered as a\n * single, provider-safe signal segment) rather than the bare `agent.invoke`.\n * DispatchBus `process()` claims pending rows by *subscribed signal type* before\n * a handler can inspect the payload, so a processor targeting worker A\n * (subscribed to `agent.invoke.<A>`) can never claim a worker-B invocation\n * (`agent.invoke.<B>`). A generic processor that handles every class subscribes\n * to the single-segment wildcard `agent.invoke.*`.\n */\nexport function agentInvokeSignalType(agentClass: string): string {\n const segment = agentClass.replace(/[^A-Za-z0-9_-]/g, '-') || 'unknown';\n return `${AGENT_INVOKE_SIGNAL}.${segment}`;\n}\n\n/**\n * A tool executed under a {@link PrincipalRun} that is not a manifest CRUD\n * operation — e.g. the orchestration invoke-agent tool. It carries its own AI\n * definition and handler, and is offered through the conversational tool loop\n * alongside manifest tools, gated by the same fail-closed `allowedTools`.\n *\n * Defined here (in `@happyvertical/smrt-agents`) rather than in the chat loop so\n * the acyclic `chat → agents` dependency direction is preserved: the loop\n * imports this contract, agents produces implementations of it.\n */\nexport interface PrincipalTool {\n /** Tool name + permission slug, gated by the persona's `allowedTools`. */\n slug: string;\n /** The provider tool definition offered to the model. */\n aiTool: AITool;\n /** Execute the tool under the principal run (should re-assert its own gate). */\n execute(ctx: PrincipalToolContext): Promise<unknown>;\n}\n\n/** Context handed to a {@link PrincipalTool.execute}. */\nexport interface PrincipalToolContext {\n /** The principal run whose context bounds this execution. */\n run: PrincipalRun;\n /** Parsed tool arguments. */\n args: Record<string, unknown>;\n /** The database handle for side-door operations. */\n db?: SmrtClassOptions['db'];\n}\n\n/** A worker invocation handed to a {@link WorkerRunner}. */\nexport interface WorkerInvocation {\n /** The principal run the worker executes within (the delegated principal). */\n run: PrincipalRun;\n /** The delegation envelope (principal, depth, correlation). */\n envelope: DelegationEnvelope;\n /** The target worker agent class. */\n agentClass: string;\n /** The task payload handed to the worker. */\n task: Record<string, unknown>;\n /** The database handle for the worker's operations. */\n db?: SmrtClassOptions['db'];\n}\n\n/**\n * Performs a worker's actual work under the delegated principal. Injected so\n * orchestration stays decoupled from *what* a worker does (run an `Agent`, run a\n * nested persona conversation, call a domain method); the runner receives a\n * {@link PrincipalRun} already bound to the originating user's permissions.\n */\nexport type WorkerRunner = (invocation: WorkerInvocation) => Promise<unknown>;\n\n/**\n * A worker's completion, correlated back to the invocation that produced it.\n */\nexport interface AgentCompletion {\n /** Correlates this completion to the invocation. */\n correlationId: string;\n /** The worker agent class that ran. */\n agentClass: string;\n /** The originating user the worker acted on behalf of. */\n onBehalfOfUserId: string;\n /** Whether the worker's work succeeded. */\n ok: boolean;\n /** The worker's result, when it succeeded. */\n result?: unknown;\n /** The error message, when it failed. */\n error?: string;\n}\n\n/** The outcome the invoke-agent tool returns to the conversation. */\nexport interface InvokeAgentResult {\n /**\n * `completed` / `failed` for an in-process (inline) invocation whose result is\n * surfaced in the same turn; `enqueued` for an async transport whose\n * completion is surfaced later via {@link surfaceAgentCompletions}.\n */\n status: 'completed' | 'failed' | 'enqueued';\n /** Correlates a later completion dispatch back to this invocation. */\n correlationId: string;\n /** The worker agent class invoked. */\n agentClass: string;\n /** The delegation depth of the invoked worker. */\n depth: number;\n /** The worker's result, when it completed in-process. */\n result?: unknown;\n /** The error message, when it failed in-process. */\n error?: string;\n}\n\n/** A delivery handed to an {@link InvokeAgentTransport}. */\nexport interface InvokeAgentDelivery {\n /** The child delegation envelope for the worker. */\n envelope: DelegationEnvelope;\n /** The target worker agent class. */\n agentClass: string;\n /** The task payload for the worker. */\n task: Record<string, unknown>;\n /** The worker runner (used by in-process transports; ignored by async ones). */\n worker: WorkerRunner;\n /** The database handle. */\n db?: SmrtClassOptions['db'];\n /** DispatchBus for the correlated invoke/completion signals. */\n dispatchBus?: DispatchBus;\n /** Audit sink forwarded to {@link executeAsPrincipal}. */\n audit?: PrincipalAuditSink;\n /** Opt into Postgres RLS transaction wrapping. */\n postgresRls?: boolean;\n /** Logger for the default audit sink. */\n logger?: Logger;\n}\n\n/**\n * How a worker invocation is delivered: run it in-process now (inline), emit a\n * DispatchBus signal for a worker to process, or enqueue a job. Swapping the\n * transport never changes the principal-delegation or completion semantics.\n */\nexport interface InvokeAgentTransport {\n deliver(delivery: InvokeAgentDelivery): Promise<InvokeAgentResult>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Whether a value is a structurally-valid {@link DelegationEnvelope} — a\n * concrete principal (`runAsUserId`), a `string | null` tenant, an originating\n * `onBehalfOfUserId`, and an integer `depth`. Used to reject a malformed\n * envelope arriving from a persisted (untrusted) dispatch payload before it\n * drives a worker.\n */\nfunction isValidDelegationEnvelope(\n value: unknown,\n): value is DelegationEnvelope {\n if (!isRecord(value)) {\n return false;\n }\n return (\n typeof value.runAsUserId === 'string' &&\n value.runAsUserId.length > 0 &&\n (value.tenantId === null || typeof value.tenantId === 'string') &&\n typeof value.onBehalfOfUserId === 'string' &&\n value.onBehalfOfUserId.length > 0 &&\n Number.isInteger(value.depth) &&\n typeof value.correlationId === 'string'\n );\n}\n\n/**\n * Run a worker as the delegated principal and report its completion.\n *\n * The worker executes inside a single {@link executeAsPrincipal} context bound\n * to the envelope's principal (`runAsUserId` + `tenantId`) and acting **on\n * behalf of** the originating user — so its authority is the originating user's\n * live RBAC and every action audits back to that user. On completion (success\n * or failure) a correlated `agent.completed` dispatch is emitted **inside** the\n * principal's tenant context, so it is stamped with the right tenant and the\n * orchestrator can surface it back into the conversation.\n */\nexport async function executeDelegatedInvocation(options: {\n envelope: DelegationEnvelope;\n agentClass: string;\n task: Record<string, unknown>;\n worker: WorkerRunner;\n db?: SmrtClassOptions['db'];\n dispatchBus?: DispatchBus;\n audit?: PrincipalAuditSink;\n postgresRls?: boolean;\n logger?: Logger;\n}): Promise<AgentCompletion> {\n const {\n envelope,\n agentClass,\n task,\n worker,\n db,\n dispatchBus,\n audit,\n postgresRls,\n logger,\n } = options;\n\n return executeAsPrincipal(\n {\n db,\n principal: {\n // The principal is the envelope's — the originating user, immutable.\n runAsUserId: envelope.runAsUserId,\n tenantId: envelope.tenantId,\n allowedTools: envelope.allowedTools,\n },\n onBehalfOfUserId: envelope.onBehalfOfUserId,\n agentClass,\n action: 'agent.invoke',\n auditMetadata: {\n correlationId: envelope.correlationId,\n depth: envelope.depth,\n },\n audit,\n postgresRls,\n logger,\n },\n async (run): Promise<AgentCompletion> => {\n let completion: AgentCompletion;\n try {\n const result = await worker({ run, envelope, agentClass, task, db });\n completion = {\n correlationId: envelope.correlationId,\n agentClass,\n onBehalfOfUserId: envelope.onBehalfOfUserId,\n ok: true,\n result,\n };\n } catch (error) {\n completion = {\n correlationId: envelope.correlationId,\n agentClass,\n onBehalfOfUserId: envelope.onBehalfOfUserId,\n ok: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n // Emit the correlated completion inside the tenant context so it is\n // stamped with the principal's tenant (readable by the orchestrator).\n if (dispatchBus) {\n await emitAgentCompletion(dispatchBus, completion);\n }\n return completion;\n },\n );\n}\n\n/**\n * Emit a correlated `agent.completed` dispatch for a worker's completion.\n */\nexport async function emitAgentCompletion(\n dispatchBus: DispatchBus,\n completion: AgentCompletion,\n): Promise<void> {\n await dispatchBus.emit(\n AGENT_COMPLETED_SIGNAL,\n {\n agentClass: completion.agentClass,\n onBehalfOfUserId: completion.onBehalfOfUserId,\n ok: completion.ok,\n result: completion.result,\n error: completion.error,\n },\n {\n correlationId: completion.correlationId,\n source: completion.agentClass || 'agent',\n },\n );\n}\n\n/**\n * Read the correlated completions for an invocation, so the orchestrator can\n * surface a worker's result back into the conversation on a later turn (the\n * async transport). Returns `[]` when nothing has completed yet.\n */\nexport async function surfaceAgentCompletions(\n dispatchBus: DispatchBus,\n correlationId: string,\n): Promise<AgentCompletion[]> {\n const dispatches = await dispatchBus.list({\n type: AGENT_COMPLETED_SIGNAL,\n correlationId,\n });\n return dispatches.map((dispatch) => {\n const payload = dispatch.payload as Record<string, unknown>;\n return {\n correlationId,\n agentClass:\n typeof payload.agentClass === 'string' ? payload.agentClass : '',\n onBehalfOfUserId:\n typeof payload.onBehalfOfUserId === 'string'\n ? payload.onBehalfOfUserId\n : '',\n ok: payload.ok === true,\n result: payload.result,\n error: typeof payload.error === 'string' ? payload.error : undefined,\n };\n });\n}\n\n/**\n * The default transport: run the worker in-process now and return its completion\n * as the tool observation, so the result is surfaced back into the conversation\n * in the same turn.\n */\nexport const inlineInvokeAgentTransport: InvokeAgentTransport = {\n async deliver(delivery): Promise<InvokeAgentResult> {\n const completion = await executeDelegatedInvocation({\n envelope: delivery.envelope,\n agentClass: delivery.agentClass,\n task: delivery.task,\n worker: delivery.worker,\n db: delivery.db,\n dispatchBus: delivery.dispatchBus,\n audit: delivery.audit,\n postgresRls: delivery.postgresRls,\n logger: delivery.logger,\n });\n return {\n status: completion.ok ? 'completed' : 'failed',\n correlationId: completion.correlationId,\n agentClass: completion.agentClass,\n depth: delivery.envelope.depth,\n result: completion.result,\n error: completion.error,\n };\n },\n};\n\n/**\n * An async transport that emits a correlated, **per-worker** `agent.invoke.<class>`\n * DispatchBus signal for a worker to process out of band\n * ({@link processAgentInvocations}). The tool returns `enqueued`; the worker's\n * completion is surfaced later via {@link surfaceAgentCompletions}. The worker\n * runner is *not* used here — it is reconstructed on the processing side.\n *\n * Emitting on the per-worker signal type (see {@link agentInvokeSignalType})\n * means a processor for worker A never claims an invocation targeted at worker\n * B, even under compete delivery.\n */\nexport function createDispatchInvokeTransport(\n dispatchBus: DispatchBus,\n options: { source?: string } = {},\n): InvokeAgentTransport {\n return {\n async deliver(delivery): Promise<InvokeAgentResult> {\n await dispatchBus.emit(\n agentInvokeSignalType(delivery.agentClass),\n {\n envelope: delivery.envelope,\n agentClass: delivery.agentClass,\n task: delivery.task,\n },\n {\n correlationId: delivery.envelope.correlationId,\n source: options.source ?? 'agents.orchestrator',\n },\n );\n return {\n status: 'enqueued',\n correlationId: delivery.envelope.correlationId,\n agentClass: delivery.agentClass,\n depth: delivery.envelope.depth,\n };\n },\n };\n}\n\n/**\n * Process pending `agent.invoke` signals, running each worker as its delegated\n * principal and emitting the correlated completion. This is the worker side of\n * {@link createDispatchInvokeTransport}.\n *\n * Pass `agentClass` to target a single worker class — the processor subscribes\n * to `agent.invoke.<class>` and can only ever claim that class's invocations, so\n * running one processor per worker class never cross-claims. Omit it for a\n * generic processor that handles every class (subscribes to the wildcard\n * `agent.invoke.*` and dispatches on the payload's `agentClass`).\n *\n * The envelope arrives from a (persisted, thus untrusted) dispatch payload, so\n * it is validated ({@link isValidDelegationEnvelope}) and its depth re-asserted\n * before the worker runs — a malformed or tampered envelope cannot drive the\n * chain past {@link MAX_DELEGATION_DEPTH}.\n *\n * @returns The number of invocations processed.\n */\nexport async function processAgentInvocations(options: {\n dispatchBus: DispatchBus;\n subscriber: string;\n worker: WorkerRunner;\n /** Target a single worker class; omit for a handle-every-class processor. */\n agentClass?: string;\n db?: SmrtClassOptions['db'];\n audit?: PrincipalAuditSink;\n postgresRls?: boolean;\n logger?: Logger;\n limit?: number;\n}): Promise<number> {\n const { dispatchBus, subscriber, worker, db, audit, postgresRls, logger } =\n options;\n const log = logger ?? createLogger({ level: 'info' });\n // Targeted processors subscribe to their own class's signal; a generic\n // processor uses the single-segment wildcard to handle every class.\n const signalType = options.agentClass\n ? agentInvokeSignalType(options.agentClass)\n : `${AGENT_INVOKE_SIGNAL}.*`;\n await dispatchBus.subscribe({ signalType, subscriber });\n return dispatchBus.process(\n subscriber,\n async (payload) => {\n const record = isRecord(payload) ? payload : {};\n const envelope = record.envelope;\n const agentClass =\n typeof record.agentClass === 'string' ? record.agentClass : '';\n // Reject a malformed/tampered payload before it drives a worker.\n if (!isValidDelegationEnvelope(envelope) || !agentClass) {\n log.warn(\n 'agent.invoke dispatch has an invalid envelope or agentClass',\n {\n agentClass,\n },\n );\n return;\n }\n // Defense in depth: a persisted (tamperable) envelope cannot exceed the\n // depth ceiling (or carry a NaN/negative depth that bypasses it).\n assertWithinDelegationDepth(envelope.depth);\n await executeDelegatedInvocation({\n envelope,\n agentClass,\n task: isRecord(record.task) ? record.task : {},\n worker,\n db,\n dispatchBus,\n audit,\n postgresRls,\n logger,\n });\n },\n { limit: options.limit },\n );\n}\n\n/**\n * Options for {@link createInvokeAgentTool}.\n */\nexport interface CreateInvokeAgentToolOptions {\n /**\n * The **current run's** delegation envelope — the orchestrator's own (depth\n * `0`) when building the tool for a conversation, or a worker's own envelope\n * when building it for a nested/further delegation. Its principal is the\n * ceiling every child inherits; the live run context is the source of truth\n * for the principal and overrides this copy.\n */\n parentEnvelope: DelegationEnvelope;\n /** The worker runner used by in-process transports. */\n worker: WorkerRunner;\n /** Database handle for the worker's operations. */\n db?: SmrtClassOptions['db'];\n /** DispatchBus for correlated invoke/completion signals. */\n dispatchBus?: DispatchBus;\n /** Delivery transport. Defaults to {@link inlineInvokeAgentTransport}. */\n transport?: InvokeAgentTransport;\n /** Audit sink forwarded to {@link executeAsPrincipal}. */\n audit?: PrincipalAuditSink;\n /** Opt into Postgres RLS transaction wrapping. */\n postgresRls?: boolean;\n /** Logger for the default audit sink. */\n logger?: Logger;\n /** Resolve a worker's tool ceiling from its class (e.g. its persona tools). */\n resolveWorkerAllowedTools?: (agentClass: string) => string[] | undefined;\n /** Depth ceiling override (mainly for tests). */\n maxDepth?: number;\n /** Override the tool description offered to the model. */\n description?: string;\n}\n\n/**\n * Build the standard **invoke-agent** tool.\n *\n * Offered through the conversational tool loop and gated by the persona's\n * `allowedTools` (the model may only call it when `agents.invoke` is\n * allow-listed). Its handler:\n *\n * 1. re-asserts the fail-closed allow-list ({@link PrincipalRun.assertToolAllowed});\n * 2. derives the child {@link DelegationEnvelope} with the principal taken\n * **from the live run context** — never from the tool arguments — so a worker\n * cannot widen the principal, and increments the bounded depth;\n * 3. delivers the invocation via the configured transport.\n *\n * The child inherits the orchestrator's principal verbatim and acts on behalf of\n * the same originating user, so the worker runs under the originating user's\n * permissions and audits back to them.\n */\nexport function createInvokeAgentTool(\n options: CreateInvokeAgentToolOptions,\n): PrincipalTool {\n const transport = options.transport ?? inlineInvokeAgentTransport;\n return {\n slug: INVOKE_AGENT_TOOL_SLUG,\n aiTool: {\n type: 'function',\n function: {\n name: INVOKE_AGENT_FUNCTION_NAME,\n description:\n options.description ??\n 'Delegate a task to a worker agent. The worker runs under YOUR ' +\n 'principal (the originating user) — it cannot exceed your ' +\n 'permissions — and returns its completion.',\n // Note: the tool deliberately exposes NO `allowedTools` / principal\n // parameters — a worker's tool ceiling and principal are never taken\n // from model-controlled arguments (see below).\n parameters: {\n type: 'object',\n required: ['agentClass'],\n properties: {\n agentClass: {\n type: 'string',\n description: 'The worker agent class to invoke.',\n },\n task: {\n type: 'object',\n description: 'The task payload handed to the worker.',\n },\n },\n },\n },\n },\n async execute({ run, args }): Promise<InvokeAgentResult> {\n // Execution gate (defense-in-depth behind the offer gate): the persona\n // must allow-list agents.invoke.\n run.assertToolAllowed(INVOKE_AGENT_TOOL_SLUG);\n\n const agentClass =\n typeof args.agentClass === 'string' ? args.agentClass.trim() : '';\n if (!agentClass) {\n throw new Error(\"invoke-agent requires a non-empty 'agentClass'.\");\n }\n const task = isRecord(args.task) ? args.task : {};\n\n // The principal is taken from the LIVE run context, never from the tool\n // arguments — this is what makes the principal immutable along the chain:\n // a worker calling invoke-agent cannot pass a broader `runAsUserId` /\n // `tenantId`, because they are not read from `args` at all.\n const parent: DelegationEnvelope = {\n ...options.parentEnvelope,\n runAsUserId: run.context.userId ?? options.parentEnvelope.runAsUserId,\n tenantId: run.context.tenantId ?? options.parentEnvelope.tenantId,\n };\n\n // The worker's tool ceiling comes ONLY from trusted server-side policy\n // (`resolveWorkerAllowedTools`), never from the model-controlled tool\n // arguments — otherwise the model could hand the worker an arbitrary tool\n // set. Absent a resolver the worker gets NO tools (fail-closed); its\n // authority is still bounded by the originating user's RBAC regardless.\n const requestedAllowedTools =\n options.resolveWorkerAllowedTools?.(agentClass);\n\n const childEnvelope = deriveDelegationEnvelope(parent, {\n allowedTools: requestedAllowedTools,\n maxDepth: options.maxDepth,\n });\n\n return transport.deliver({\n envelope: childEnvelope,\n agentClass,\n task,\n worker: options.worker,\n db: options.db,\n dispatchBus: options.dispatchBus,\n audit: options.audit,\n postgresRls: options.postgresRls,\n logger: options.logger,\n });\n },\n };\n}\n","/**\n * Principal-bound report tools built on the shared data-surface contracts.\n *\n * Reports remain the authority for materialized queries, lifecycle, drilldown,\n * and export validation. This module only binds those contracts to a live\n * PrincipalRun and the generic data-surface catalog. Applications retain\n * authorization, audit, queue, immutable-snapshot, and browser transports.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { AITool } from '@happyvertical/ai';\nimport {\n normalizeDataQueryRequest,\n type SmrtClassOptions,\n} from '@happyvertical/smrt-core';\nimport {\n type AppliedReportExport,\n type AppliedReportRefresh,\n applyReportExport,\n applyReportRefresh,\n buildReportAdapterDescriptor,\n buildReportDrilldownQuery,\n createReportExportRequest,\n createReportExportSnapshot,\n previewReportExport,\n previewReportRefresh,\n queryReportMaterializedRows,\n type ReportAdapterDescriptor,\n type ReportAdapterOptions,\n type ReportBackgroundQueryTask,\n type ReportDataQueryResult,\n type ReportExportActionHost,\n type ReportExportPreview,\n type ReportExportSnapshotBinding,\n type ReportLifecycleOptions,\n type ReportQueryOptions,\n type ReportRefreshActionHost,\n type ReportRefreshPreview,\n} from '@happyvertical/smrt-reports';\nimport type { DataQueryRequest } from '@happyvertical/smrt-types';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport {\n createDataSurfaceTools,\n DATA_QUERY_TOOL_SLUG,\n type DataSurfaceDefinition,\n DataSurfaceDeniedError,\n type DataSurfaceExecutionContext,\n type DataSurfaceSchema,\n type DataSurfaceToolsOptions,\n} from './data-surface.js';\nimport type { PrincipalRun } from './execute-as-principal.js';\nimport type { PrincipalTool, PrincipalToolContext } from './invoke-agent.js';\n\nexport const REPORT_QUERY_TOOL_SLUG = 'reports.query';\nexport const REPORT_REFRESH_TOOL_SLUG = 'reports.refresh';\nexport const REPORT_DRILLDOWN_TOOL_SLUG = 'reports.drilldown';\nexport const REPORT_EXPORT_TOOL_SLUG = 'reports.export';\n\nexport const REPORT_QUERY_FUNCTION_NAME = 'reports-query';\nexport const REPORT_REFRESH_FUNCTION_NAME = 'reports-refresh';\nexport const REPORT_DRILLDOWN_FUNCTION_NAME = 'reports-drilldown';\nexport const REPORT_EXPORT_FUNCTION_NAME = 'reports-export';\n\ntype ReportCtor = Parameters<typeof buildReportAdapterDescriptor>[0];\ntype ReportReadOptions = Omit<\n ReportQueryOptions,\n 'adapter' | 'db' | 'execution'\n>;\n\n/** A server-authenticated browser command compatible with the chat bridge. */\nexport interface ReportDataSurfaceVisibleCommand {\n version: 1;\n commandId: string;\n identity: { surfaceId: string; kind: 'report' };\n expectedRevision: number;\n controlId: 'query';\n payload: { request: DataQueryRequest };\n}\n\n/** Browser acknowledgement is required before a visible query succeeds. */\nexport interface ReportDataSurfaceVisibleAck {\n commandId: string;\n identity: { surfaceId: string; kind: 'report' };\n ok: boolean;\n revision?: number;\n reason?: string;\n}\n\nexport interface ReportDataSurfaceVisibleHost {\n /**\n * The host must bind the command to an authenticated browser session and\n * await its acknowledgement. It must not use this command as authorization.\n */\n send(\n command: ReportDataSurfaceVisibleCommand,\n context: { run: PrincipalRun },\n ): Promise<ReportDataSurfaceVisibleAck>;\n}\n\nexport interface ReportDataSurfaceExportHost {\n /** The host captures an opaque immutable materialization binding. */\n captureSnapshot(context: {\n run: PrincipalRun;\n descriptor: ReportAdapterDescriptor;\n result: ReportDataQueryResult;\n }): Promise<ReportExportSnapshotBinding>;\n /** The returned host owns current authorization, audit, and queueing. */\n actionHost(context: {\n run: PrincipalRun;\n }): ReportExportActionHost | Promise<ReportExportActionHost>;\n}\n\nexport interface ReportDataSurfaceRefreshHost {\n /** The returned host authorizes and audits against the live principal. */\n actionHost(context: {\n run: PrincipalRun;\n }): ReportRefreshActionHost | Promise<ReportRefreshActionHost>;\n /** Optional queue tuning that remains application-owned. */\n options?: Omit<\n Parameters<typeof applyReportRefresh>[1],\n 'db' | 'host' | 'mode' | 'refreshAction'\n >;\n}\n\nexport interface ReportDataSurfaceDefinition {\n /** Report model; never supplied by a model or browser argument. */\n report: ReportCtor;\n /** Permission-catalog collection used for the shared read gate. */\n collection: string;\n label?: string;\n description?: string;\n adapter?: ReportAdapterOptions;\n /** Application-owned collection/lifecycle seam for materialized reads. */\n query?: (\n context: DataSurfaceExecutionContext,\n ) => ReportReadOptions | Promise<ReportReadOptions>;\n /** Background execution never receives principal or tenant in its task. */\n enqueueBackgroundQuery?: (\n task: ReportBackgroundQueryTask,\n context: { run: PrincipalRun },\n ) => Promise<{ taskId: string }>;\n visible?: ReportDataSurfaceVisibleHost;\n refresh?: ReportDataSurfaceRefreshHost;\n export?: ReportDataSurfaceExportHost;\n}\n\nexport interface ReportDataSurfaceAuditEntry {\n action: 'query' | 'refresh' | 'drilldown' | 'export';\n reportId: string;\n userId: string;\n tenantId: string | null;\n}\n\nexport interface ReportDataSurfaceToolsOptions {\n reports:\n | readonly ReportDataSurfaceDefinition[]\n | ((\n run: PrincipalRun,\n ) =>\n | readonly ReportDataSurfaceDefinition[]\n | Promise<readonly ReportDataSurfaceDefinition[]>);\n /** Optional audit of agent-level handoffs; report action hosts audit mutations. */\n audit?: (entry: ReportDataSurfaceAuditEntry) => void | Promise<void>;\n /** Passed through to generic data.discover/data.inspect/data.query tools. */\n dataSurface?: Omit<DataSurfaceToolsOptions, 'surfaces' | 'execute' | 'audit'>;\n}\n\nexport class ReportDataSurfaceVisibleError extends Error {\n constructor(reason?: string) {\n super(\n `Report visible command was not acknowledged${reason ? `: ${reason}` : ''}`,\n );\n this.name = 'ReportDataSurfaceVisibleError';\n }\n}\n\nexport class ReportDataSurfaceConfigurationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ReportDataSurfaceConfigurationError';\n }\n}\n\nfunction requiredString(value: unknown, label: string): string {\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new ReportDataSurfaceConfigurationError(\n `${label} must be a non-empty string`,\n );\n }\n return value;\n}\n\nfunction principalFromRun(\n run: PrincipalRun,\n): DataSurfaceExecutionContext['principal'] {\n const userId = run.context.userId;\n if (!userId) throw new DataSurfaceDeniedError();\n return { userId, tenantId: run.context.tenantId };\n}\n\nfunction reportDatabase(\n run: PrincipalRun,\n fallback: PrincipalToolContext['db'],\n): DatabaseInterface | undefined {\n const database = run.context.database ?? fallback;\n if (database === undefined) return undefined;\n if (\n typeof database !== 'object' ||\n database === null ||\n !('query' in database) ||\n typeof database.query !== 'function'\n ) {\n throw new ReportDataSurfaceConfigurationError(\n 'Report tools require a live database handle, not a database configuration',\n );\n }\n return database as DatabaseInterface;\n}\n\nfunction requireDatabase(\n run: PrincipalRun,\n fallback: PrincipalToolContext['db'],\n): DatabaseInterface {\n const database = reportDatabase(run, fallback);\n if (!database) {\n throw new ReportDataSurfaceConfigurationError(\n 'Report lifecycle and export tools require the authenticated database context',\n );\n }\n return database;\n}\n\nfunction querySchema(descriptor: ReportAdapterDescriptor): DataSurfaceSchema {\n return {\n ...descriptor.schema,\n fields: descriptor.columns.map((column) => ({\n id: column.id,\n type: column.type,\n projectable: column.projectable !== false,\n sortable: column.sortable === true,\n facetable: column.facetable === true,\n ...(column.filterOperators\n ? { filterOperators: [...column.filterOperators] }\n : {}),\n metadata: {\n kind: column.kind,\n filterScope: column.filterScope,\n capabilities: [...column.capabilities],\n ...(column.bucket ? { bucket: column.bucket } : {}),\n ...(column.aggregate ? { aggregate: column.aggregate } : {}),\n ...(column.format ? { format: column.format } : {}),\n },\n // The report descriptor is fail-closed: sensitive and permissioned\n // fields were already removed before reaching this catalog.\n })),\n };\n}\n\nfunction reportCatalogMetadata(\n definition: ReportDataSurfaceDefinition,\n descriptor: ReportAdapterDescriptor,\n run?: PrincipalRun,\n auditAvailable = false,\n): NonNullable<DataSurfaceDefinition['metadata']> {\n const canUse = (tool: string) => run?.isToolAllowed(tool) ?? true;\n const canUseAction = (tool: string, requiredPermission: string) =>\n canUse(tool) && (run?.permissions.includes(requiredPermission) ?? true);\n const canQuery =\n canUse(DATA_QUERY_TOOL_SLUG) || canUse(REPORT_QUERY_TOOL_SLUG);\n const canDrilldown = auditAvailable && canUse(REPORT_DRILLDOWN_TOOL_SLUG);\n const queryModes = descriptor.queryExecution.modes.filter(\n (mode) =>\n (mode === 'silent' && canQuery) ||\n (mode === 'background' &&\n definition.enqueueBackgroundQuery !== undefined &&\n canUse(REPORT_QUERY_TOOL_SLUG)) ||\n (mode === 'visible' &&\n definition.visible !== undefined &&\n canUse(REPORT_QUERY_TOOL_SLUG)),\n );\n const allowedActions = [\n ...(canQuery ? ['query'] : []),\n ...(definition.refresh &&\n canUseAction(\n REPORT_REFRESH_TOOL_SLUG,\n descriptor.refresh.action.requiredPermission,\n )\n ? [descriptor.refresh.action.id]\n : []),\n ...(canDrilldown ? [descriptor.drilldown.id] : []),\n ...(definition.export &&\n canUseAction(REPORT_EXPORT_TOOL_SLUG, 'reports.export')\n ? ['export']\n : []),\n ];\n return {\n surfaceKind: 'report',\n queryModes,\n filterScopes: ['where', 'having'],\n freshnessSource: 'reportLifecycle',\n freshnessAvailableWithAuthenticatedDatabase: true,\n allowedActions,\n ...(definition.refresh &&\n canUseAction(\n REPORT_REFRESH_TOOL_SLUG,\n descriptor.refresh.action.requiredPermission,\n )\n ? {\n refreshPhases: descriptor.refresh.action.phases,\n refreshRequiredPermission:\n descriptor.refresh.action.requiredPermission,\n refreshAuditRequired: descriptor.refresh.action.auditRequired,\n }\n : {}),\n ...(canDrilldown\n ? { drilldownSourceClass: descriptor.drilldown.sourceClassName }\n : {}),\n ...(definition.export &&\n canUseAction(REPORT_EXPORT_TOOL_SLUG, 'reports.export')\n ? { exportPhases: ['preview', 'apply'], exportSnapshotBound: true }\n : {}),\n };\n}\n\nasync function configuredReports(\n options: ReportDataSurfaceToolsOptions,\n run: PrincipalRun,\n): Promise<readonly ReportDataSurfaceDefinition[]> {\n return typeof options.reports === 'function'\n ? options.reports(run)\n : options.reports;\n}\n\nasync function reportEntry(\n options: ReportDataSurfaceToolsOptions,\n run: PrincipalRun,\n reportId: unknown,\n): Promise<{\n definition: ReportDataSurfaceDefinition;\n descriptor: ReportAdapterDescriptor;\n}> {\n const requested = requiredString(reportId, 'reportId');\n for (const definition of await configuredReports(options, run)) {\n const descriptor = await buildReportAdapterDescriptor(\n definition.report,\n definition.adapter,\n );\n if (descriptor.resourceId !== requested) continue;\n try {\n await run.assertOperation(definition.collection, 'read');\n return { definition, descriptor };\n } catch {\n break;\n }\n }\n // Do not reveal whether a report exists, has different field policy, or is\n // merely not readable under this principal.\n throw new DataSurfaceDeniedError();\n}\n\nasync function readOptions(\n definition: ReportDataSurfaceDefinition,\n context: DataSurfaceExecutionContext,\n): Promise<ReportReadOptions> {\n return (await definition.query?.(context)) ?? {};\n}\n\nasync function audit(\n options: ReportDataSurfaceToolsOptions,\n action: ReportDataSurfaceAuditEntry['action'],\n reportId: string,\n run: PrincipalRun,\n): Promise<void> {\n await options.audit?.({\n action,\n reportId,\n userId: principalFromRun(run).userId,\n tenantId: run.context.tenantId,\n });\n}\n\nfunction aiTool(\n slug: string,\n name: string,\n description: string,\n parameters: Record<string, unknown>,\n execute: (context: PrincipalToolContext) => Promise<unknown>,\n): PrincipalTool {\n return {\n slug,\n aiTool: {\n type: 'function',\n function: { name, description, parameters },\n } satisfies AITool,\n execute,\n };\n}\n\n/**\n * Convert a report adapter into the safe generic catalog consumed by\n * data.discover/data.inspect/data.query. Generic agent queries are always\n * silent; browser changes require the explicit report query tool below.\n */\nexport async function createReportDataSurfaceDefinition(\n definition: ReportDataSurfaceDefinition,\n run?: PrincipalRun,\n auditAvailable = false,\n): Promise<DataSurfaceDefinition> {\n const descriptor = await buildReportAdapterDescriptor(\n definition.report,\n definition.adapter,\n );\n return {\n id: descriptor.resourceId,\n collection: definition.collection,\n className: descriptor.reportClassName,\n label: definition.label ?? descriptor.reportClassName,\n ...(definition.description ? { description: definition.description } : {}),\n metadata: reportCatalogMetadata(\n definition,\n descriptor,\n run,\n auditAvailable,\n ),\n schema: querySchema(descriptor),\n execute: async (_surface, request, context) => {\n const db = reportDatabase(context.run, context.db);\n const result = await queryReportMaterializedRows(\n definition.report,\n request,\n {\n ...(await readOptions(definition, context)),\n adapter: definition.adapter,\n ...(db ? { db, lifecycle: lifecycleOptions(db) } : {}),\n execution: 'silent',\n },\n );\n const {\n execution: _execution,\n reportLifecycle: _lifecycle,\n ...data\n } = result;\n return data;\n },\n };\n}\n\nasync function dataSurfaceCatalog(\n options: ReportDataSurfaceToolsOptions,\n run: PrincipalRun,\n): Promise<readonly DataSurfaceDefinition[]> {\n return Promise.all(\n (await configuredReports(options, run)).map((definition) =>\n createReportDataSurfaceDefinition(\n definition,\n run,\n typeof options.audit === 'function',\n ),\n ),\n );\n}\n\nfunction commandResultIsBound(\n ack: unknown,\n command: ReportDataSurfaceVisibleCommand,\n): ack is ReportDataSurfaceVisibleAck {\n if (typeof ack !== 'object' || ack === null) return false;\n const value = ack as Record<string, unknown>;\n if (typeof value.identity !== 'object' || value.identity === null) {\n return false;\n }\n const identity = value.identity as Record<string, unknown>;\n return (\n value.commandId === command.commandId &&\n identity.surfaceId === command.identity.surfaceId &&\n identity.kind === command.identity.kind &&\n typeof value.revision === 'number' &&\n Number.isSafeInteger(value.revision) &&\n value.revision >= command.expectedRevision\n );\n}\n\nfunction commandFailureReason(ack: unknown): string | undefined {\n if (typeof ack !== 'object' || ack === null) return undefined;\n const reason = (ack as Record<string, unknown>).reason;\n return typeof reason === 'string' ? reason : undefined;\n}\n\nfunction lifecycleOptions(\n db: NonNullable<SmrtClassOptions['db']>,\n): Omit<ReportLifecycleOptions, 'db'> {\n // Lifecycle has no caller-controlled authority fields. This small helper\n // makes the export tool request the report's tenant-safe freshness snapshot.\n void db;\n return {};\n}\n\n/**\n * Build generic report discovery/query tools plus report-only operational\n * tools. Every report is looked up from a server-owned catalog per live run.\n */\nexport function createReportDataSurfaceTools(\n options: ReportDataSurfaceToolsOptions,\n): PrincipalTool[] {\n const generic = createDataSurfaceTools({\n ...options.dataSurface,\n surfaces: (run) => dataSurfaceCatalog(options, run),\n });\n\n const query = aiTool(\n REPORT_QUERY_TOOL_SLUG,\n REPORT_QUERY_FUNCTION_NAME,\n 'Run a bounded report query silently, in the background, or with an acknowledged browser update.',\n {\n type: 'object',\n required: ['reportId', 'request'],\n properties: {\n reportId: { type: 'string' },\n request: { type: 'object' },\n execution: { enum: ['silent', 'background', 'visible'] },\n expectedRevision: { type: 'integer', minimum: 0 },\n },\n additionalProperties: false,\n },\n async ({ run, args, db }) => {\n run.assertToolAllowed(REPORT_QUERY_TOOL_SLUG);\n const { definition, descriptor } = await reportEntry(\n options,\n run,\n args.reportId,\n );\n const request = normalizeDataQueryRequest(\n args.request,\n descriptor.schema,\n );\n let execution: 'silent' | 'background' | 'visible' = 'silent';\n if (args.execution !== undefined) {\n if (\n args.execution !== 'silent' &&\n args.execution !== 'background' &&\n args.execution !== 'visible'\n ) {\n throw new ReportDataSurfaceConfigurationError(\n 'Report query execution is invalid',\n );\n }\n execution = args.execution;\n }\n const context: DataSurfaceExecutionContext = {\n run,\n principal: principalFromRun(run),\n db,\n signal: new AbortController().signal,\n };\n const reportDb = reportDatabase(run, db);\n const base = {\n ...(await readOptions(definition, context)),\n ...(reportDb\n ? { db: reportDb, lifecycle: lifecycleOptions(reportDb) }\n : {}),\n };\n if (execution === 'background') {\n if (!definition.enqueueBackgroundQuery) {\n throw new ReportDataSurfaceConfigurationError(\n 'This report does not expose a background query host',\n );\n }\n const result = await queryReportMaterializedRows(\n definition.report,\n request,\n {\n ...base,\n adapter: definition.adapter,\n execution: 'background',\n enqueueBackgroundQuery: (task) =>\n definition.enqueueBackgroundQuery?.(task, { run }) ??\n Promise.reject(\n new ReportDataSurfaceConfigurationError(\n 'This report does not expose a background query host',\n ),\n ),\n },\n );\n await audit(options, 'query', descriptor.resourceId, run);\n return result;\n }\n\n const result =\n execution === 'silent'\n ? await queryReportMaterializedRows(definition.report, request, {\n ...base,\n adapter: definition.adapter,\n execution: 'silent',\n })\n : await queryReportMaterializedRows(definition.report, request, {\n ...base,\n adapter: definition.adapter,\n execution: 'visible',\n });\n if (execution === 'silent') {\n await audit(options, 'query', descriptor.resourceId, run);\n return result;\n }\n\n if (!definition.visible) {\n throw new ReportDataSurfaceConfigurationError(\n 'This report does not expose a browser-visible query host',\n );\n }\n const expectedRevision = args.expectedRevision;\n if (\n typeof expectedRevision !== 'number' ||\n !Number.isSafeInteger(expectedRevision) ||\n expectedRevision < 0\n ) {\n throw new ReportDataSurfaceConfigurationError(\n 'Visible report queries require a non-negative expectedRevision',\n );\n }\n const command: ReportDataSurfaceVisibleCommand = {\n version: 1,\n commandId: randomUUID(),\n identity: { surfaceId: descriptor.resourceId, kind: 'report' },\n expectedRevision,\n controlId: 'query',\n payload: { request },\n };\n const acknowledged = await definition.visible.send(command, { run });\n if (\n !commandResultIsBound(acknowledged, command) ||\n acknowledged.ok !== true\n ) {\n throw new ReportDataSurfaceVisibleError(\n commandFailureReason(acknowledged),\n );\n }\n await audit(options, 'query', descriptor.resourceId, run);\n return { ...result, browser: acknowledged };\n },\n );\n\n const refresh = aiTool(\n REPORT_REFRESH_TOOL_SLUG,\n REPORT_REFRESH_FUNCTION_NAME,\n 'Preview or apply an authorized, audited report refresh.',\n {\n type: 'object',\n required: ['reportId', 'phase'],\n properties: {\n reportId: { type: 'string' },\n phase: { enum: ['preview', 'apply'] },\n mode: { enum: ['rebuild', 'incremental'] },\n },\n additionalProperties: false,\n },\n async ({ run, args, db }) => {\n run.assertToolAllowed(REPORT_REFRESH_TOOL_SLUG);\n const { definition, descriptor } = await reportEntry(\n options,\n run,\n args.reportId,\n );\n if (!definition.refresh) {\n throw new ReportDataSurfaceConfigurationError(\n 'This report does not expose refresh',\n );\n }\n if (args.phase !== 'preview' && args.phase !== 'apply') {\n throw new ReportDataSurfaceConfigurationError(\n 'Report refresh phase is invalid',\n );\n }\n const mode =\n args.mode === undefined\n ? undefined\n : args.mode === 'rebuild' || args.mode === 'incremental'\n ? args.mode\n : (() => {\n throw new ReportDataSurfaceConfigurationError(\n 'Report refresh mode is invalid',\n );\n })();\n const host = await definition.refresh.actionHost({ run });\n const lifecycleDb = requireDatabase(run, db);\n const result: ReportRefreshPreview | AppliedReportRefresh =\n args.phase === 'preview'\n ? await previewReportRefresh(definition.report, {\n db: lifecycleDb,\n host,\n mode,\n refreshAction: descriptor.refresh.action,\n })\n : await applyReportRefresh(definition.report, {\n db: lifecycleDb,\n host,\n mode,\n ...definition.refresh.options,\n refreshAction: descriptor.refresh.action,\n });\n await audit(options, 'refresh', descriptor.resourceId, run);\n return result;\n },\n );\n\n const drilldown = aiTool(\n REPORT_DRILLDOWN_TOOL_SLUG,\n REPORT_DRILLDOWN_FUNCTION_NAME,\n 'Create a principal-bound source drilldown from one readable materialized report row.',\n {\n type: 'object',\n required: ['reportId', 'rowId'],\n properties: { reportId: { type: 'string' }, rowId: { type: 'string' } },\n additionalProperties: false,\n },\n async ({ run, args, db }) => {\n run.assertToolAllowed(REPORT_DRILLDOWN_TOOL_SLUG);\n if (typeof options.audit !== 'function') {\n throw new ReportDataSurfaceConfigurationError(\n 'Report drilldown requires a live audit sink',\n );\n }\n const { definition, descriptor } = await reportEntry(\n options,\n run,\n args.reportId,\n );\n const rowId = requiredString(args.rowId, 'rowId');\n const reportDb = reportDatabase(run, db);\n const context: DataSurfaceExecutionContext = {\n run,\n principal: principalFromRun(run),\n db: reportDb,\n signal: new AbortController().signal,\n };\n const result = await queryReportMaterializedRows(\n definition.report,\n {\n version: 1,\n requestId: randomUUID(),\n mode: 'rows',\n projection: descriptor.drilldown.fields.map((field) => field.id),\n filter: {\n kind: 'condition',\n field: 'id',\n operator: 'eq',\n value: rowId,\n },\n page: { kind: 'offset', offset: 0, limit: 1 },\n },\n {\n ...(await readOptions(definition, context)),\n adapter: definition.adapter,\n ...(reportDb ? { db: reportDb } : {}),\n execution: 'silent',\n },\n );\n if (result.rows.length !== 1) throw new DataSurfaceDeniedError();\n const handoff = await buildReportDrilldownQuery(\n definition.report,\n result.rows[0],\n definition.adapter,\n );\n await audit(options, 'drilldown', descriptor.resourceId, run);\n return handoff;\n },\n );\n\n const exportTool = aiTool(\n REPORT_EXPORT_TOOL_SLUG,\n REPORT_EXPORT_FUNCTION_NAME,\n 'Preview or apply a principal-bound, snapshot-verified report export.',\n {\n type: 'object',\n required: ['reportId', 'phase', 'query', 'format'],\n properties: {\n reportId: { type: 'string' },\n phase: { enum: ['preview', 'apply'] },\n query: { type: 'object' },\n format: { enum: ['csv', 'json'] },\n limits: { type: 'object' },\n confirmed: { type: 'boolean' },\n },\n additionalProperties: false,\n },\n async ({ run, args, db }) => {\n run.assertToolAllowed(REPORT_EXPORT_TOOL_SLUG);\n const { definition, descriptor } = await reportEntry(\n options,\n run,\n args.reportId,\n );\n if (!definition.export) {\n throw new ReportDataSurfaceConfigurationError(\n 'This report does not expose export',\n );\n }\n if (args.phase !== 'preview' && args.phase !== 'apply') {\n throw new ReportDataSurfaceConfigurationError(\n 'Report export phase is invalid',\n );\n }\n const exportDb = requireDatabase(run, db);\n const context: DataSurfaceExecutionContext = {\n run,\n principal: principalFromRun(run),\n db: exportDb,\n signal: new AbortController().signal,\n };\n const result = await queryReportMaterializedRows(\n definition.report,\n args.query,\n {\n ...(await readOptions(definition, context)),\n adapter: definition.adapter,\n db: exportDb,\n lifecycle: lifecycleOptions(exportDb),\n execution: 'silent',\n },\n );\n const binding = await definition.export.captureSnapshot({\n run,\n descriptor,\n result,\n });\n const snapshot = createReportExportSnapshot(\n descriptor,\n args.query,\n result,\n binding,\n );\n const request = createReportExportRequest(descriptor, snapshot, {\n format: args.format,\n ...(args.limits === undefined ? {} : { limits: args.limits }),\n });\n const host = await definition.export.actionHost({ run });\n const exported: ReportExportPreview | AppliedReportExport =\n args.phase === 'preview'\n ? await previewReportExport(descriptor, request, host)\n : await applyReportExport(descriptor, request, host, {\n confirmed: args.confirmed === true,\n });\n await audit(options, 'export', descriptor.resourceId, run);\n return exported;\n },\n );\n\n return [...generic, query, refresh, drilldown, exportTool];\n}\n","import {\n field,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport {\n queryGlobal,\n queryWithGlobals,\n TenantScoped,\n tenantId,\n} from '@happyvertical/smrt-tenancy';\nimport {\n getAgentClassName,\n getAgentTypeAliases,\n getAgentTypeName,\n} from './identity.js';\n\n/**\n * Status of a scheduled agent\n */\nexport type ScheduleStatus = 'active' | 'paused' | 'disabled' | 'error';\n\n/**\n * AgentSchedule model for cron-based agent scheduling\n *\n * This extends SmrtObject to store schedule metadata in the SMRT database.\n * Schedules are processed by the TaskRunner which creates jobs at scheduled times.\n *\n * @example\n * ```typescript\n * const schedule = new AgentSchedule({\n * agentType: 'Praeco',\n * agentId: 'praeco-main',\n * cron: '0 2 * * *', // Run at 2 AM daily\n * enabled: true,\n * });\n * await schedule.initialize();\n * await schedule.save();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: '_smrt_agent_schedules',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: {\n include: ['list', 'get', 'create', 'update', 'delete', 'enable', 'disable'],\n // enable/disable are operator commands invoked in-process via the CLI;\n // they intentionally aren't exposed over HTTP.\n skipApiCheck: true,\n },\n mcp: { include: ['list', 'get'] },\n // ScheduleRunner.poll() scans this predicate every minute (#2364, epic\n // #2382 finding A3): `enabled = true AND status = 'active' AND\n // next_run <= ?`, ordered by `next_run ASC`. `enabled` and `status` are\n // both equality filters and lead `next_run`, the range filter: a B-tree\n // composite serves an equality prefix as a direct lookup but can only\n // range-scan its trailing column, so the two equality columns come first\n // (the composite still serves `status`-only and `(enabled, status)`-only\n // reads as leftward prefixes).\n indexes: [\n {\n name: '_smrt_agent_schedules_enabled_status_next_run_idx',\n columns: ['enabled', 'status', 'nextRun'],\n },\n ],\n})\nexport class AgentSchedule extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n * Nullable to support both tenant-scoped and global schedules\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Canonical agent type to run (qualified name when available) */\n @field({ type: 'text' })\n agentType: string = '';\n\n /** Optional agent instance ID (for running specific instances) */\n @field({ type: 'text', nullable: true })\n agentId: string | null = null;\n\n /**\n * Agent configuration to pass when running.\n *\n * Sensitive (#1540): may carry API keys/credentials, so it is excluded from\n * generated API/MCP responses and rejected as a `where` filter key.\n */\n @field({ type: 'json', sqlType: 'TEXT', sensitive: true })\n agentConfig: Record<string, unknown> = {};\n\n /** Cron expression (e.g., '0 2 * * *' for 2 AM daily) */\n @field({ type: 'text' })\n cron: string = '';\n\n /** Timezone for cron interpretation (default: UTC) */\n @field({ type: 'text' })\n timezone: string = 'UTC';\n\n /** Whether the schedule is enabled */\n @field({ type: 'boolean' })\n enabled: boolean = true;\n\n /** Current schedule status */\n @field({ type: 'text' })\n status: ScheduleStatus = 'active';\n\n /** Last time the agent was run */\n @field({ type: 'datetime', nullable: true })\n lastRun: Date | null = null;\n\n /** Next scheduled run time */\n @field({ type: 'datetime', nullable: true })\n nextRun: Date | null = null;\n\n /** Status of the last run */\n @field({ type: 'text', nullable: true })\n lastStatus: 'success' | 'failed' | null = null;\n\n /** Error message from last failed run */\n @field({ type: 'text', nullable: true })\n lastError: string | null = null;\n\n /** Total number of runs */\n @field({ type: 'integer' })\n runCount: number = 0;\n\n /** Total number of successful runs */\n @field({ type: 'integer' })\n successCount: number = 0;\n\n /** Total number of failed runs */\n @field({ type: 'integer' })\n failureCount: number = 0;\n\n /** Maximum concurrent runs (prevent overlapping) */\n @field({ type: 'integer' })\n maxConcurrent: number = 1;\n\n /** Current number of running instances */\n @field({ type: 'integer' })\n runningCount: number = 0;\n\n /** Timeout for agent execution in milliseconds (default: 1 hour) */\n @field({ type: 'integer' })\n timeout: number = 3600000;\n\n /** Method to call on the agent (default: 'run') */\n @field({ type: 'text' })\n method: string = 'run';\n\n /** Arguments to pass to the method */\n @field({ type: 'json', sqlType: 'TEXT' })\n methodArgs: Record<string, unknown> = {};\n\n /**\n * Enable the schedule\n */\n async enable(): Promise<void> {\n this.enabled = true;\n this.status = 'active';\n this.calculateNextRun();\n await this.save();\n }\n\n /**\n * Disable the schedule\n */\n async disable(): Promise<void> {\n this.enabled = false;\n this.status = 'disabled';\n await this.save();\n }\n\n /**\n * Pause the schedule temporarily\n */\n async pause(): Promise<void> {\n this.status = 'paused';\n await this.save();\n }\n\n /**\n * Resume a paused schedule\n */\n async resume(): Promise<void> {\n if (this.enabled) {\n this.status = 'active';\n this.calculateNextRun();\n }\n await this.save();\n }\n\n /**\n * Calculate the next run time based on cron expression\n */\n calculateNextRun(): void {\n if (!this.cron || !this.enabled) {\n this.nextRun = null;\n return;\n }\n\n try {\n const next = getNextCronDate(this.cron, this.timezone);\n this.nextRun = next;\n } catch {\n this.nextRun = null;\n this.status = 'error';\n this.lastError = `Invalid cron expression: ${this.cron}`;\n }\n }\n\n /**\n * Get a human-readable description of the schedule\n */\n getDescription(): string {\n const displayAgentType = getAgentClassName(this.agentType);\n const agent = this.agentId\n ? `${displayAgentType}#${this.agentId}`\n : displayAgentType;\n return `${agent}.${this.method}() @ ${this.cron}`;\n }\n\n /**\n * Lifecycle hook - calculate next run on save\n */\n async beforeSave(): Promise<void> {\n if (this.agentType) {\n this.agentType = getAgentTypeName(this.agentType);\n }\n if (!this.nextRun && this.enabled) {\n this.calculateNextRun();\n }\n }\n}\n\n/**\n * Collection for managing AgentSchedule objects\n */\nexport class AgentScheduleCollection extends SmrtCollection<AgentSchedule> {\n static readonly _itemClass = AgentSchedule;\n\n /**\n * Find all schedules for a specific tenant\n * @param tenantId - Tenant ID to filter by\n * @returns Array of AgentSchedule objects for the tenant\n */\n async findByTenant(tenantId: string): Promise<AgentSchedule[]> {\n return this.list({ where: { tenantId } });\n }\n\n /**\n * Find all global schedules (not associated with any tenant).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global AgentSchedule objects\n */\n async findGlobal(): Promise<AgentSchedule[]> {\n return queryGlobal<AgentSchedule>(this);\n }\n\n /**\n * Find schedules for a tenant including global schedules.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - Tenant ID to include\n * @returns Array of AgentSchedule objects for the tenant and global schedules\n */\n async findWithGlobals(tenantId: string): Promise<AgentSchedule[]> {\n return queryWithGlobals<AgentSchedule>(\n this,\n tenantId,\n 'AgentSchedule.findWithGlobals',\n );\n }\n\n /**\n * List schedules by status\n */\n async listByStatus(\n status: ScheduleStatus | ScheduleStatus[],\n options: { limit?: number } = {},\n ): Promise<AgentSchedule[]> {\n return this.list({\n where: {\n status: Array.isArray(status) ? status : [status],\n },\n orderBy: 'next_run ASC',\n limit: options.limit,\n });\n }\n\n /**\n * List schedules for a specific agent type\n */\n async listByAgentType(\n agentType: string,\n options: { limit?: number; includeDisabled?: boolean } = {},\n ): Promise<AgentSchedule[]> {\n const aliases = getAgentTypeAliases(agentType);\n const where: Record<string, unknown> =\n aliases.length > 1\n ? { 'agentType in': aliases }\n : { agentType: getAgentTypeName(agentType) };\n if (!options.includeDisabled) {\n where.enabled = true;\n }\n\n return this.list({\n where,\n orderBy: 'next_run ASC',\n limit: options.limit,\n });\n }\n}\n\n/**\n * Parse a cron expression and get the next run date.\n *\n * Supports standard 5-field cron format: minute hour day-of-month month\n * day-of-week. Day-of-month / day-of-week follow POSIX OR semantics when both\n * are restricted (see the loop body). Matched against the host's local time\n * (not timezone-aware).\n *\n * Examples:\n * - '0 2 * * *' - 2:00 AM daily\n * - '0 0 * * 0' - Midnight on Sundays\n * - 'x/15 * * * *' - Every 15 minutes (where x is asterisk)\n * - '0 9 1 * *' - 9:00 AM on the 1st of every month\n *\n * Exported for unit testing of the matching logic.\n */\nexport function getNextCronDate(cron: string, _timezone: string = 'UTC'): Date {\n const parts = cron.trim().split(/\\s+/);\n if (parts.length !== 5) {\n throw new Error(\n `Invalid cron expression: expected 5 fields, got ${parts.length}`,\n );\n }\n\n const [minuteExpr, hourExpr, dayExpr, monthExpr, dowExpr] = parts;\n\n const now = new Date();\n const candidate = new Date(now);\n candidate.setSeconds(0);\n candidate.setMilliseconds(0);\n\n // Move to next minute at minimum\n candidate.setMinutes(candidate.getMinutes() + 1);\n\n // Standard cron DOM/DOW semantics:\n // When both day-of-month and day-of-week are restricted (not *),\n // a date matches if EITHER condition is met (OR logic). When only one\n // is restricted, only that field applies; when both are `*`, every day\n // matches. POSIX: `0 0 13 * 5` fires on the 13th OR any Friday.\n const dayIsWildcard = dayExpr === '*';\n const dowIsWildcard = dowExpr === '*';\n\n // Search for next matching date (limit to 1 year)\n const maxIterations = 525600; // ~1 year in minutes\n for (let i = 0; i < maxIterations; i++) {\n const dayMatches = matchesCronField(candidate.getDate(), dayExpr);\n // getDay() returns 0 for Sunday; standard cron accepts both 0 and 7\n const dow = candidate.getDay();\n const dowMatches =\n matchesCronField(dow, dowExpr) ||\n (dow === 0 && matchesCronField(7, dowExpr));\n\n let dayOfMonthOrWeekMatches: boolean;\n if (!dayIsWildcard && !dowIsWildcard) {\n dayOfMonthOrWeekMatches = dayMatches || dowMatches;\n } else if (!dayIsWildcard) {\n dayOfMonthOrWeekMatches = dayMatches;\n } else if (!dowIsWildcard) {\n dayOfMonthOrWeekMatches = dowMatches;\n } else {\n dayOfMonthOrWeekMatches = true;\n }\n\n if (\n matchesCronField(candidate.getMonth() + 1, monthExpr) &&\n dayOfMonthOrWeekMatches &&\n matchesCronField(candidate.getHours(), hourExpr) &&\n matchesCronField(candidate.getMinutes(), minuteExpr)\n ) {\n return candidate;\n }\n\n candidate.setMinutes(candidate.getMinutes() + 1);\n }\n\n throw new Error(`Could not find next run date for cron: ${cron}`);\n}\n\n/**\n * Check if a value matches a cron field expression\n */\nfunction matchesCronField(value: number, expr: string): boolean {\n // Wildcard matches everything\n if (expr === '*') {\n return true;\n }\n\n // Handle step values (*/5, 0-30/2)\n if (expr.includes('/')) {\n const [range, stepStr] = expr.split('/');\n const step = parseInt(stepStr, 10);\n if (range === '*') {\n return value % step === 0;\n }\n // Handle range with step\n if (range.includes('-')) {\n const [startStr, endStr] = range.split('-');\n const start = parseInt(startStr, 10);\n const end = parseInt(endStr, 10);\n if (value < start || value > end) return false;\n return (value - start) % step === 0;\n }\n }\n\n // Handle ranges (1-5)\n if (expr.includes('-')) {\n const [startStr, endStr] = expr.split('-');\n const start = parseInt(startStr, 10);\n const end = parseInt(endStr, 10);\n return value >= start && value <= end;\n }\n\n // Handle lists (1,3,5)\n if (expr.includes(',')) {\n const values = expr.split(',').map((v) => parseInt(v.trim(), 10));\n return values.includes(value);\n }\n\n // Exact match\n return value === parseInt(expr, 10);\n}\n\nexport default AgentSchedule;\n","/**\n * TenantAgent - Junction between tenants and agents\n *\n * Represents the binding of an agent class to a specific tenant,\n * with optional permission overrides and status control.\n *\n * The absence of a row means \"check parent tenant\" — inheritance\n * is a resolution behavior, not stored state.\n */\n\nimport {\n field,\n SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport {\n getAgentClassName,\n getAgentTypeAliases,\n getAgentTypeName,\n} from './identity.js';\nimport type { AgentManifestInfo } from './ui.js';\n\n/**\n * Status of a tenant-agent binding\n */\nexport type TenantAgentStatus = 'active' | 'disabled';\n\n/**\n * Permission definition for merge logic\n */\ninterface PermissionDef {\n id: string;\n defaultGranted?: boolean;\n}\n\n/**\n * Result of resolving agent availability for a tenant\n */\nexport interface ResolvedAgentAvailability {\n /** Human-readable agent class name (e.g., 'Praeco') */\n agentClass: string;\n /** Canonical agent type (qualified name when available) */\n agentType: string;\n /** Resolved status */\n status: TenantAgentStatus;\n /** How this was resolved */\n source: 'explicit' | 'inherited';\n /** Which tenant the binding came from */\n sourceTenantId: string;\n /** Merged permissions (manifest defaults overridden by explicit grants/revokes) */\n permissions: Record<string, boolean>;\n /** The agent instance ID (row in agents table), if one exists */\n agentId?: string;\n /** Agent manifest from the build (if available) */\n manifest?: AgentManifestInfo;\n /** Tenant-level config overrides */\n config?: Record<string, unknown>;\n}\n\n/**\n * TenantAgent SmrtObject — junction between tenants and agents\n *\n * Each row represents an explicit binding of an agent class to a tenant.\n * - Presence means explicit override (active or disabled)\n * - Absence means \"check parent tenant\" (inheritance)\n *\n * Permission overrides:\n * - null/missing key → use defaultGranted from manifest\n * - true → explicitly granted\n * - false → explicitly revoked\n */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'tenant_agents',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n cli: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n conflictColumns: ['tenant_id', 'agent_class'],\n})\nexport class TenantAgent extends SmrtObject {\n @tenantId()\n tenantId: string = '';\n\n /** Canonical agent type (qualified name when available) */\n @field({ type: 'text' })\n agentClass: string = '';\n\n /** Status of the agent for this tenant */\n @field({ type: 'text' })\n status: TenantAgentStatus = 'active';\n\n /** Explicit permission overrides (JSON). null = use manifest defaults */\n @field({ type: 'json', nullable: true })\n permissions: Record<string, boolean> | null = null;\n\n /**\n * Tenant-level agent config overrides (JSON).\n *\n * Sensitive (S5 #1398): like {@link AgentConfig.configData} and\n * {@link AgentSchedule.agentConfig} (both marked sensitive in #1540), these\n * per-tenant override blobs routinely carry API keys/credentials. Exclude\n * them from generated API/MCP responses and reject them as a `where` filter\n * key. Server-side helpers (e.g. `serializeResolvedAgent`) still read the\n * property directly, so the admin dashboard flow is unaffected.\n */\n @field({ type: 'json', nullable: true, sensitive: true })\n config: Record<string, unknown> | null = null;\n}\n\n/**\n * Collection for managing tenant-agent bindings\n */\nexport class TenantAgentCollection extends SmrtCollection<TenantAgent> {\n static readonly _itemClass = TenantAgent;\n\n /**\n * Resolve agent availability for a tenant, walking up the hierarchy.\n *\n * Algorithm:\n * 1. Load explicit entries for this tenant\n * 2. Build result map from explicit entries (source = 'explicit')\n * 3. Merge permissions: manifest defaults overridden by explicit permissions\n * 4. Get tenant's ancestors via hierarchyPath (immediate parent → root)\n * 5. For each ancestor, add inherited agents not already resolved\n * 6. Return only agents that appear somewhere in the hierarchy\n *\n * @param tenantId - The tenant to resolve for\n * @param getAncestorIds - Function that returns ancestor tenant IDs (parent → root order)\n * @param manifests - Map of agent class name to AgentManifestInfo\n */\n async resolveForTenant(\n tenantId: string,\n getAncestorIds: (tenantId: string) => Promise<string[]>,\n manifests?: Map<string, AgentManifestInfo>,\n ): Promise<ResolvedAgentAvailability[]> {\n const result = new Map<string, ResolvedAgentAvailability>();\n\n // Step 1: Load explicit entries for this tenant\n const explicitEntries = await this.list({\n where: { tenantId },\n });\n\n // Step 2: Build result from explicit entries\n for (const entry of explicitEntries) {\n const agentType = await this.normalizeStoredAgentClass(entry);\n const manifest = getManifestForAgent(manifests, agentType);\n const mergedPermissions = mergePermissions(\n manifest?.permissions,\n entry.permissions,\n );\n\n result.set(agentType, {\n agentClass: getAgentClassName(agentType),\n agentType,\n status: entry.status,\n source: 'explicit',\n sourceTenantId: tenantId,\n permissions: mergedPermissions,\n manifest,\n config: entry.config ?? undefined,\n });\n }\n\n // Step 3: Walk ancestors for inherited agents\n const ancestorIds = await getAncestorIds(tenantId);\n for (const ancestorId of ancestorIds) {\n const ancestorEntries = await this.list({\n where: { tenantId: ancestorId },\n });\n\n for (const entry of ancestorEntries) {\n const agentType = await this.normalizeStoredAgentClass(entry);\n // Skip if already resolved explicitly or from a closer ancestor\n if (result.has(agentType)) continue;\n\n const manifest = getManifestForAgent(manifests, agentType);\n const mergedPermissions = mergePermissions(\n manifest?.permissions,\n entry.permissions,\n );\n\n result.set(agentType, {\n agentClass: getAgentClassName(agentType),\n agentType,\n status: entry.status,\n source: 'inherited',\n sourceTenantId: ancestorId,\n permissions: mergedPermissions,\n manifest,\n config: entry.config ?? undefined,\n });\n }\n }\n\n return Array.from(result.values());\n }\n\n /**\n * Enable an agent for a tenant (creates or updates binding)\n */\n async enableAgent(\n tenantId: string,\n agentClass: string,\n ): Promise<TenantAgent> {\n const canonicalAgentClass = getAgentTypeName(agentClass);\n const existing = await this.findByTenantAndClass(tenantId, agentClass);\n if (existing) {\n existing.status = 'active';\n await existing.save();\n return existing;\n }\n\n const entry = await this.create({\n tenantId,\n agentClass: canonicalAgentClass,\n status: 'active',\n });\n await entry.save();\n return entry;\n }\n\n /**\n * Disable an agent for a tenant\n */\n async disableAgent(\n tenantId: string,\n agentClass: string,\n ): Promise<TenantAgent> {\n const canonicalAgentClass = getAgentTypeName(agentClass);\n const existing = await this.findByTenantAndClass(tenantId, agentClass);\n if (existing) {\n existing.status = 'disabled';\n await existing.save();\n return existing;\n }\n\n const entry = await this.create({\n tenantId,\n agentClass: canonicalAgentClass,\n status: 'disabled',\n });\n await entry.save();\n return entry;\n }\n\n /**\n * Remove explicit override, falling back to inheritance\n */\n async clearOverride(tenantId: string, agentClass: string): Promise<void> {\n const existing = await this.findByTenantAndClass(tenantId, agentClass);\n if (existing) {\n await existing.delete();\n }\n }\n\n /**\n * Set permission overrides for a tenant's agent binding\n */\n async setPermissions(\n tenantId: string,\n agentClass: string,\n permissions: Record<string, boolean>,\n ): Promise<TenantAgent> {\n const canonicalAgentClass = getAgentTypeName(agentClass);\n const existing = await this.findByTenantAndClass(tenantId, agentClass);\n if (existing) {\n existing.permissions = permissions;\n await existing.save();\n return existing;\n }\n\n const entry = await this.create({\n tenantId,\n agentClass: canonicalAgentClass,\n status: 'active',\n permissions,\n });\n await entry.save();\n return entry;\n }\n\n /**\n * Find a tenant-agent binding by tenant and agent class\n */\n async findByTenantAndClass(\n tenantId: string,\n agentClass: string,\n ): Promise<TenantAgent | null> {\n const aliases = getAgentTypeAliases(agentClass);\n const results = await this.list({\n where:\n aliases.length > 1\n ? { tenantId, 'agentClass in': aliases }\n : { tenantId, agentClass: aliases[0] },\n });\n\n const canonicalAgentClass = getAgentTypeName(agentClass);\n const found =\n results.find((entry) => entry.agentClass === canonicalAgentClass) ||\n results[0] ||\n null;\n\n if (found && found.agentClass !== canonicalAgentClass) {\n await this.persistCanonicalAgentClass(found, canonicalAgentClass);\n }\n\n return found;\n }\n\n private async normalizeStoredAgentClass(entry: TenantAgent): Promise<string> {\n const canonicalAgentClass = getAgentTypeName(entry.agentClass);\n if (entry.agentClass !== canonicalAgentClass) {\n await this.persistCanonicalAgentClass(entry, canonicalAgentClass);\n }\n return canonicalAgentClass;\n }\n\n private async persistCanonicalAgentClass(\n entry: TenantAgent,\n canonicalAgentClass: string,\n ): Promise<void> {\n if (!entry.id || entry.agentClass === canonicalAgentClass) {\n entry.agentClass = canonicalAgentClass;\n return;\n }\n\n await this._db.query(\n `UPDATE ${this.tableName}\n SET agent_class = ?,\n updated_at = ?\n WHERE id = ?`,\n canonicalAgentClass,\n new Date().toISOString(),\n entry.id,\n );\n\n entry.agentClass = canonicalAgentClass;\n }\n}\n\n/**\n * Merge manifest permission defaults with explicit overrides\n */\nfunction mergePermissions(\n manifestPermissions?: PermissionDef[],\n overrides?: Record<string, boolean> | null,\n): Record<string, boolean> {\n const result: Record<string, boolean> = {};\n\n // Start with manifest defaults\n if (manifestPermissions) {\n for (const perm of manifestPermissions) {\n result[perm.id] = perm.defaultGranted !== false;\n }\n }\n\n // Apply overrides\n if (overrides) {\n for (const [key, value] of Object.entries(overrides)) {\n result[key] = value;\n }\n }\n\n return result;\n}\n\nfunction getManifestForAgent(\n manifests: Map<string, AgentManifestInfo> | undefined,\n agentTypeOrIdentifier: string,\n): AgentManifestInfo | undefined {\n if (!manifests) {\n return undefined;\n }\n\n return (\n manifests.get(agentTypeOrIdentifier) ||\n manifests.get(getAgentClassName(agentTypeOrIdentifier))\n );\n}\n"],"mappings":"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"}