/** * Dormant `AgentSchedule` installation for the #2051 learning loop. * * `@happyvertical/smrt-agents` is an OPTIONAL dependency of this package * (the smrt-users seam): agents sits ABOVE fields in the dependency DAG (it * hard-depends on smrt-users, ai, and secrets — exactly the packages fields * keeps optional), so the installer dynamic-imports it and degrades * gracefully (`installed: false`) when it is not present. The schedule TARGET * (`FieldUsageLearningAgent`) needs no agents import at all — smrt-jobs * resolves `agentType` through the `ObjectRegistry`. * * DORMANT BY DEFAULT (the epic's suggestion-first posture): schedules are * created with `enabled: false` / `status: 'disabled'` unless the caller * explicitly opts in with `enabled: true` (or later runs the AgentSchedule * `enable()` operator command / flips the row). Activation is a deliberate * per-deployment decision documented in this package's AGENTS.md. */ import type { DatabaseInterface } from '@happyvertical/sql'; import type { FieldUsageMaintenanceConfig, FieldUsageSuggestionConfig } from './usage-learning.js'; /** Registry-qualified schedule target (`AgentSchedule.agentType`). */ export declare const FIELD_USAGE_LEARNING_AGENT_TYPE = "@happyvertical/smrt-fields:FieldUsageLearningAgent"; /** Method the aggregation/retention schedule invokes. */ export declare const FIELD_USAGE_MAINTENANCE_METHOD = "runUsageMaintenance"; /** Method the suggestion-generation schedule invokes. */ export declare const FIELD_USAGE_SUGGESTION_METHOD = "runSuggestionGeneration"; /** * Default cadence for counter maintenance: daily at 02:30 UTC (the * `AgentSchedule.timezone` default). See {@link ensureFieldUsageLearningSchedules}. */ export declare const DEFAULT_FIELD_USAGE_MAINTENANCE_CRON = "30 2 * * *"; /** Default cadence for suggestion generation: weekly, Monday 03:00 UTC. */ export declare const DEFAULT_FIELD_USAGE_SUGGESTION_CRON = "0 3 * * 1"; /** * Stable id for a global learning schedule, derived from the agent type and * method (the `TenantUsageMetric.recordUsage` precedent). * * `_smrt_agent_schedules` has no natural-key uniqueness on * `(agent_type, method)`, so a check-then-create would let two replicas * starting against an empty database each insert their own random-id row — * and with `enabled: true` every job would then run twice. A deterministic id * makes the insert converge on ONE primary key instead. */ export declare function fieldUsageScheduleId(method: string): Promise; /** * Structural surface of the agents module the installer consumes (no static * import — mirrors `FieldPolicyUsersModule`). `create`/`list`/`save` are the * standard SmrtCollection/SmrtObject shapes. */ export interface FieldUsageAgentsScheduleRow { id?: string | null; enabled?: boolean; /** * Owning tenant; `null`/absent marks the GLOBAL schedules this installer * manages. Read so the existence check cannot mistake a tenant-specific * schedule for the global one. */ tenantId?: string | null; save?: () => Promise; } export interface FieldUsageAgentsModule { AgentScheduleCollection: { create(options: { db: DatabaseInterface; }): Promise<{ list(options: { where: Record; }): Promise; create(data: Record): Promise; }>; }; } export interface EnsureFieldUsageLearningSchedulesOptions { db: DatabaseInterface; /** * Whether the schedules start enabled. DEFAULT FALSE — the learning loop * ships dormant; enabling it is an explicit deployment opt-in. */ enabled?: boolean; maintenanceCron?: string; suggestionCron?: string; /** Threshold/retention overrides persisted into the schedules' methodArgs. */ maintenanceArgs?: Partial; suggestionArgs?: Partial; /** Injection seam for tests / hosts that already loaded the agents module. */ agentsModule?: FieldUsageAgentsModule; } export interface EnsureFieldUsageLearningSchedulesResult { /** False when `@happyvertical/smrt-agents` is not installed (no-op). */ installed: boolean; /** Schedules created by THIS call (existing rows are left untouched). */ created: number; } /** * Idempotently create the two GLOBAL (tenant-null) `AgentSchedule` rows for * the learning loop — the aggregation/retention roll-up and the * suggestion-generation job. An existing GLOBAL row for the agent type + * method is never modified (operator state like enable/disable is preserved). * * The existence check is scoped to the GLOBAL rows deliberately: a deployment * may also run tenant-specific schedules for the same agent type and method * (the ambient-context runs the jobs support), and matching one of those would * silently skip installing the global schedule this function promises. Tenant * rows are read but never touched. * * Concurrency: each schedule is written under a DETERMINISTIC id * ({@link fieldUsageScheduleId}) with insert-only semantics, so two replicas * installing at once converge on one row — the pre-check is only the cheap * path, and a primary-key collision is treated as "already installed" rather * than an error. Insert-only also means an existing row's operator state * (enabled/disabled, edited cron) is never overwritten by a later install. * * Time zone: rows are installed with the `AgentSchedule.timezone` default, * `UTC`, and both `calculateNextRun()` and smrt-jobs' `ScheduleRunner` * evaluate the cron in the row's zone (#3859), so the crons above fire in * UTC regardless of the scheduler host's `TZ`. An operator may set the row's * `timezone`; insert-only installs never overwrite that edit. * * System operation: global schedules are platform state, so a non-bypass * ambient tenant context is rejected (fail closed) — call this from trusted * startup/migration code. */ export declare function ensureFieldUsageLearningSchedules(options: EnsureFieldUsageLearningSchedulesOptions): Promise; //# sourceMappingURL=usage-schedules.d.ts.map