/** * Run-group contract - the fan-out / dark-automation layer shared between * hosts (Skaile platform first; a CLI host later) and UI clients. * * A RunGroup is job control: one flow template executed N times across N * inputs, each execution ("RunInstance") in its own isolated session. The * flow-execution contract (`./flow.ts`) is the program INSIDE one instance; * this contract schedules and aggregates across instances. The two are * deliberately distinct - see docs/superpowers/specs/2026-07-16-run-group-contract-design.md. * * Live truth is owned by the HOST (there is no single runner for a group); * the pure helpers in `@skaile/workspaces/run-group-engine` operate on these * shapes without I/O. */ import type { ArtifactRef } from "./flow.js"; /** Where an input (and therefore an instance) may originate. */ export type TriggerSource = "manual" | "webhook" | "agent" | "schedule"; /** * Host-interpreted declarative workspace/session configuration (the * skaile.yaml shape: image, connectors, skills, model, egress, ...). * Opaque at the contract level - hosts validate against their own config * schema. Kept as a named alias so a stricter type can replace it without * touching consumers. */ export type WorkspaceRecipe = Record; /** * Opaque principal reference. Hosts map to their identity model * (Skaile platform: Keycloak role/group/userId). The framework never * enforces authority - hosts do. */ export type PrincipalRef = string; /** * How one InputEnvelope reaches the flow inside an instance. * `params` -> flow user_inputs/globals; `file` -> staged into the * workspace; `both` -> both. `filePath` is required whenever files are * staged (`file` / `both`). */ export type InputBinding = { as: "params"; /** JSONPath from the payload to a flow param, e.g. `{ contractId: "$.id" }`. */ paramMap?: Record; } | { as: "file"; /** Stage location inside the workspace, e.g. `input/contract.pdf`. */ filePath: string; } | { as: "both"; /** JSONPath from the payload to a flow param, e.g. `{ contractId: "$.id" }`. */ paramMap?: Record; /** Stage location inside the workspace, e.g. `input/contract.pdf`. */ filePath: string; }; export interface RunTemplate { /** Asset ref, e.g. `flow:@getec/contract-migration`. */ flow: string; /** Pinned at group creation (same rule as startFlow). */ flowVersion: string; workspace: WorkspaceRecipe; inputBinding: InputBinding; } export interface RunGroupPolicy { /** Admission cap: max instances in `spawning`/`running` at once. */ maxParallel: number; /** Passed to each instance's startFlow. */ autonomousMode: boolean; /** Automatic re-queue budget per instance. */ maxRetries: number; /** Minutes; host hard-kills and marks failed (kind: infra). */ instanceTimeout?: number; /** Decimal cost in the host's reporting currency (Skaile platform v1: USD); host auto-pauses the group at the cap. */ maxBudget?: number; /** Allowed input sources; default `['manual']`. */ triggers?: TriggerSource[]; /** Who may create/activate/resume the group. Host-enforced. */ startableBy?: PrincipalRef[]; /** Who may decide instance gates; drives inbox routing. Host-enforced. */ approvers?: PrincipalRef[]; } export type RunGroupMode = "batch" | "standing"; /** * `complete`/`failed`: batch only. `closed`: standing only. */ export type RunGroupStatus = "draft" | "active" | "paused" | "complete" | "failed" | "cancelled" | "closed"; export type InstanceStatus = "queued" | "spawning" | "running" | "awaiting_approval" | "awaiting_input" | "complete" | "failed" | "cancelled"; export interface RunGroup { groupId: string; name: string; mode: RunGroupMode; status: RunGroupStatus; template: RunTemplate; policy: RunGroupPolicy; /** Derived by `rollup`; denormalized for display. */ counters: Record; createdAt: string; /** Last snapshot write; debugging aid. */ updatedAt: string; /** userId | trigger endpoint id | agent. */ createdBy: string; } export interface InputEnvelope { inputId: string; /** JSON payload from webhook/UI/agent/schedule. */ payload: unknown; /** Staged file refs. */ files?: string[]; source: TriggerSource; /** At-least-once sender protection. Scope: per-group, group lifetime. */ dedupeKey?: string; /** * UTC ISO 8601 (Z-suffixed); admit() sorts lexicographically, non-UTC * offsets would misorder. */ receivedAt: string; /** * Admission rank, higher first. Absent means 0, so an input written before * this field existed queues exactly where it always did. Any signed integer * is accepted - deliberately unbounded, so a caller never has to know the * range in use to jump a queue. */ priority?: number; } export interface RunInstanceError { message: string; recoverable: boolean; /** * `flow` = the flow failed on content; `infra` = container death, * timeout, spawn failure. Same retry path today; kept distinct for * backoff/alerting and the circuit breaker (which counts only `infra`). */ kind: "flow" | "infra"; } export interface RunInstanceMetrics { startedAt?: string; completedAt?: string; costTokens?: number; /** Decimal cost in the host's reporting currency (Skaile platform v1: USD). */ costCurrency?: number; } export interface RunInstance { instanceId: string; groupId: string; input: InputEnvelope; status: InstanceStatus; /** Host session id, set at spawning. */ sessionRef?: string; /** Set once start_flow fires; the instance's `flow:${runId}` store. */ flowRunId?: string; /** 1..maxRetries+1. */ attempt: number; error?: RunInstanceError; metrics?: RunInstanceMetrics; /** Flow contract's ArtifactRef (`./flow.ts`). */ artifacts?: ArtifactRef[]; } //# sourceMappingURL=run-group.d.ts.map