import { SerializationFormat } from '../../common/constants/http.constant'; import { TriggerTypeValue } from './triggers'; export type TriggerType = TriggerTypeValue; /** * Wire-level `trigger_type` union (schema-validated by * `eaera_cms/core/rules/definitions.py:33-39`). * * Note this is **different** from the `source_type` value the engine matcher * reads (`event_dto | cron | batch | stream | signal`). The canonical * pair for event rules is `trigger_type: "event"` + * `source_type: "event_dto"` — see `packs/registry.py:463-470` and the * SDK `TRIGGER_TYPE` / `TRIGGER_MODE` constants in `triggers.ts`. * * Aliased to `TriggerType` via the `index.ts` barrel for backwards * compatibility with consumers that imported it from this module. */ /** * One row in `trigger.conditions[]` — the flat AND list the engine evaluates * when `conditionType` is `'trigger_conds'` or omitted. The dashboard's visual * tree builder (`LogicTree.vue`) emits these; backend-authored packs (`*.yaml`) * emit them too. `field` is either a CH catalog key (`account_state.equity`), * a dotted event-payload path (`deal.profit`), or a flat event field * (`profit` — engine fallback). * * `operator` is one of the word forms exposed by the SDK * (`CONDITION_OPERATORS`: `gt`/`gte`/`lt`/`lte`/`eq`/`ne`/`in`/`not_in`/ * `contains`/`startswith`/`endswith`/`is_null`/`is_not_null`) plus the * cron-specific `sum_within_days` (carries a numeric `days` window). * * `value` shape depends on the operator — see * `cms-sdk/src/services/rules/conditions.ts:CONDITION_OPERATOR_VALUE_KINDS`: * - numeric operators → number * - `in` / `not_in` → array of primitives * - string operators → string (entity is coerced via `str()`) * - `is_null` / `is_not_null` → ignored (send `null`) * - `sum_within_days` → number (threshold), `days` is the window */ export interface TriggerConditionRow { field: string; operator: string; value: unknown; /** Cron-only: window size for `sum_within_days`. */ days?: number; } export interface TriggerDefinition { /** * The backend SQL filter (`trigger_api.py:fire_signal`) and engine matcher * (`engine.py:_match_trigger`) read `trigger.source_type`, NOT * `trigger.trigger_type`. Builders in `triggers.ts` emit BOTH * `sourceType` and `triggerType` (camelCase) so the CMS casing middleware * produces both keys in the stored JSONB. * * IMPORTANT: for event rules the two values DIVERGE — see the canonical * contract documented on `TRIGGER_MODE` / `TRIGGER_TYPE` in * `cms-sdk/src/services/rules/triggers.ts`. `sourceType: 'event_dto'` * (engine matcher), `triggerType: 'event'` (schema validator). Earlier * SDK builds emitted both as `'event_dto'`, which drifted from the * pack-authored contract. */ sourceType?: 'event_dto' | 'cron' | 'batch' | 'stream' | 'signal'; triggerType: TriggerType; topic?: string; /** * Top-level AND condition list. Backend reads this when * `conditionType` is `'trigger_conds'` (or omitted). Dashboard tree * builder emits here; the Fixed Rules editor's `trigger_conds` path * goes through here too. */ conditions?: TriggerConditionRow[]; /** * Type-specific trigger config ONLY — e.g. `signalName`, `topic`, `schedule`, * `timezone`, `streamName`, `windowSeconds`, `contentTypes`, `eventTypes`, * `promptName` (LLM rules), `queryTools` (LLM rules). * * There is **no `scope` concept** on a rule. A rule is scoped by `contentType` * + trigger-source matching + the `conditionExpr`; do not write a `scope` * (`groups`/`symbols`/`logins`/`emails`) key here — the backend ignores it. */ sourceConfig: Record; } /** Value-level constants for ConditionType — single source of truth. */ export declare const CONDITION_TYPE: { readonly EXPRESSION: "expression"; readonly CODE: "code"; readonly LLM_AGENT: "llm_agent"; readonly TRIGGER_CONDS: "trigger_conds"; }; /** Condition type — derived from {@link CONDITION_TYPE}. */ export type ConditionType = (typeof CONDITION_TYPE)[keyof typeof CONDITION_TYPE]; /** Value-level constants for ExecutionType — single source of truth. */ export declare const EXECUTION_TYPE: { readonly ACTION: "action"; readonly SET_FIELD: "set_field"; readonly NOTIFY: "notify"; readonly TRANSITION: "transition"; readonly HTTP_CALL: "http_call"; readonly CHAIN: "chain"; }; /** Execution type — derived from {@link EXECUTION_TYPE}. */ export type ExecutionType = (typeof EXECUTION_TYPE)[keyof typeof EXECUTION_TYPE]; /** * One action invocation. The dispatcher reads `actionName` and a **nested** * `params`; e.g. a group move is `{ actionName: 'change_group', params: { newGroup } }` * and a leverage change is `{ actionName: 'change_leverage', params: { newLeverage } }`. */ export interface ActionExecutionParams { actionName: string; /** Action-specific payload (`{ newGroup }` / `{ newLeverage }`). */ params: Record; } /** * One step in an ordered action chain. Each step dispatches via its own * `type` (any `ExecutionType` except `chain`); an `action` step uses * {@link ActionExecutionParams} as its `params`. */ export interface ChainStep { type: Exclude; params: Record; } /** * Flat execution params stored on `ContentRule.executionParams`. The backend * dispatcher (`dispatch_target`) selects behaviour by `executionType`: * * - `action` → `{ actionName, params }` * - `set_field` → `{ field, value }` * - `notify` → `{ data?, notificationType?, recipient? }` * - `transition`→ `{ transitionName, params }` * - `http_call` → `{ url, method?, params?, headers?, timeout? }` * - `chain` → `{ steps: ChainStep[], stepParams?: Record> }` * * Action parameters are **nested** under `params` — `{ actionName, newGroup }` * (flat) is silently dropped. See {@link ActionExecutionParams}. */ export interface ExecutionParams { actionName?: string; params?: Record; field?: string; value?: unknown; data?: Record; notificationType?: string; recipient?: string; transitionName?: string; url?: string; method?: string; headers?: Record; timeout?: number; steps?: ChainStep[]; stepParams?: Record>; [key: string]: unknown; } /** POST /api/v1/rules body (flat — matches `create_trigger()`). */ export interface RuleCreatePayload { name: string; description?: string; /** `null`/omitted = global rule. Backend reads `contentType`, not `typeName`. */ contentType?: string | null; trigger: TriggerDefinition; /** Default `'expression'`. */ conditionType?: ConditionType; /** Inline Python expression (conditionType=expression). */ conditionExpr?: string | null; /** Full Python code block (conditionType=code). */ conditionCode?: string | null; /** Default `'action'`. */ executionType?: ExecutionType; executionParams?: ExecutionParams | null; cronSchedule?: string | null; cronTimeZone?: string | null; /** Default `0`. */ priority?: number; /** Default `true`. */ isActive?: boolean; /** 1-3600, default 30. */ timeoutSeconds?: number; } /** PUT /api/v1/rules/{uuid} body (all optional, same flat keys). */ export interface RuleUpdatePayload { name?: string; description?: string; contentType?: string | null; trigger?: TriggerDefinition; conditionType?: ConditionType; conditionExpr?: string | null; conditionCode?: string | null; executionType?: ExecutionType; executionParams?: ExecutionParams | null; priority?: number; isActive?: boolean; timeoutSeconds?: number; } /** GET /api/v1/rules, GET /api/v1/rules/{uuid} (`ContentRuleResponse`). */ export interface ContentRule { id: number; uuid: string; name: string; description?: string | null; /** Echoes `contentType` on the response. */ typeName?: string | null; /** Default `'python'`. */ codeType: string; /** AES-256-GCM encrypted. */ conditionCode?: string | null; conditionType?: ConditionType; conditionExpr?: string | null; /** Inline trigger JSONB (`{ sourceType, sourceConfig, conditions, settings }`). */ trigger: Record; executionType: ExecutionType; executionParams?: Record | null; priority: number; isActive: boolean; timeoutSeconds: number; /** Cron: top-level normalized schedule. Dashboard emits via * `trigger.sourceConfig.schedule`; the backend reads both. */ cronSchedule?: string | null; cronTimezone?: string | null; /** ISO 8601. */ nextTriggerAt?: string | null; /** ISO 8601 — FEAT-004 sidebar "last triggered". */ lastFiredAt?: string | null; packId?: string | null; createdBy?: number | null; updatedBy?: number | null; createdAt?: string | null; updatedAt?: string | null; } export interface ContentRuleListResponse { items: ContentRule[]; /** `=== items.length` — the list endpoint does NOT paginate. */ total: number; } /** * Signal-fire context (`POST /api/v1/rules/signals/fire`). Carries the target * account + any extra payload the engine/condition evaluator reads. A `null` * `contentUuid` means scan-mode (the LLM discovers candidates itself). */ export interface SignalFireContext { /** Content type of the target (e.g. `'Mt5Account'`). */ typeName?: string; /** Target account UUID, or `null` for scan-mode `llm_agent` rules. */ contentUuid?: string | null; /** Extra payload (e.g. `{ serverName, login }`). */ data?: Record; } /** `POST /api/v1/rules/signals/fire` response. */ export interface SignalFireResponse { /** Number of rules that matched and were dispatched. */ triggeredCount: number; /** UUIDs of the matched rules. */ matchedRules: string[]; } /** Query params for GET /api/v1/rules (route-level Query() params; no limit/offset). */ export interface RuleListQuery { sourceType?: string; executionType?: ExecutionType; /** contentType filter. */ typeName?: string; isActive?: boolean; /** UUID or slug (FEAT-008 v1.1). */ packId?: string; format?: SerializationFormat; } //# sourceMappingURL=rules.type.d.ts.map