import type { KeyId } from '@earendil-works/pi-tui'; export const WORKFLOW_SCHEMA_VERSION = 1 as const; export const DEFAULT_STATUS_SHORTCUT = 'ctrl+alt+w' as const satisfies KeyId; export const AGENT_PROFILE_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)*$/; export const MAX_WORKSPACE_PATH_CHARS = 4_096; export const MAX_WORKSPACE_ALLOWED_ROOTS = 32; export const TERMINAL_TARGETS = ['$done', '$pause'] as const; export type TerminalTarget = (typeof TERMINAL_TARGETS)[number]; export type StepTarget = string; export type BashMode = 'deny' | 'allow-list' | 'unrestricted'; export type BashRule = { readonly executable: string; readonly argsPrefix: ReadonlyArray; }; export type BashPermission = { readonly mode: BashMode; readonly allow: ReadonlyArray; }; export type StepPermissions = { /** Exact Pi tool names, including direct MCP tools. */ readonly tools: ReadonlyArray; /** MCP proxy selectors in `server` or `server/tool` form. */ readonly mcp: ReadonlyArray; /** Skills the step prompt is allowed to use. */ readonly skills: ReadonlyArray; readonly bash: BashPermission; }; export type StepAgent = { /** Workflow-owned role prompt applied to this main-agent step. */ readonly name: string; }; export type PromptSpec = { readonly file: string }; export type RequiredHeading = { readonly level: 1 | 2 | 3; readonly title: string; readonly guidance: string; }; export type ArtifactContract = { readonly maxChars: number; readonly requiredHeadings: ReadonlyArray; }; type GateDefinition = { /** Gates accept only the model's `ready` result. */ readonly submitOutcome: 'ready'; /** Approval follows the step's `ready` transition. */ readonly approvedOutcome: 'ready'; /** Rejection revisits the active step through its `handoff` transition. */ readonly rejectedOutcome: 'handoff'; readonly artifactContract?: ArtifactContract; }; export type WorkflowGate = GateDefinition & { readonly timeoutMs: number; }; export type StepWorkspaceBinding = { /** Must contain only `ready`; retained in the persisted schema for compatibility. */ readonly bindOn: ReadonlyArray; /** Relative, absolute, or ~/ home-relative paths that may contain the workspace. */ readonly allowedRoots: ReadonlyArray; }; export type WorkflowStep = { readonly title: string; readonly prompt: PromptSpec; /** Optional workflow-owned role prompt for this main-agent step. */ readonly agent?: StepAgent; readonly maxToolCalls?: number; readonly permissions: StepPermissions; readonly transitions: Readonly>; readonly gate?: WorkflowGate; /** Optional immutable workspace binding produced by this delegated step. */ readonly workspace?: StepWorkspaceBinding; }; export type WorkflowDefinition = { readonly version: typeof WORKFLOW_SCHEMA_VERSION; readonly id: string; readonly command: string; readonly description: string; readonly start: string; readonly maxStepVisits: number; readonly summaryMaxChars: number; readonly steps: Readonly>; }; export type WorkflowSourceKind = 'user'; export type LoadedWorkflow = { readonly definition: WorkflowDefinition; readonly prompts: Readonly>; readonly digest: string; readonly stepDigests: Readonly>; /** Per-step digest excluding only the prompt specification and resolved text. */ readonly stepStructuralDigests: Readonly>; readonly sourcePath: string; readonly sourceKind: WorkflowSourceKind; }; export type WorkflowSettings = { readonly version: typeof WORKFLOW_SCHEMA_VERSION; readonly statusShortcut: KeyId; }; export type ConfigDiagnostic = { readonly level: 'warning' | 'error'; readonly path: string; readonly message: string; }; export type WorkflowCatalog = { readonly workflows: ReadonlyMap; readonly settings: WorkflowSettings; readonly diagnostics: ReadonlyArray; readonly userDirectory: string; }; export const EMPTY_PERMISSIONS = { tools: [], mcp: [], skills: [], bash: { mode: 'deny', allow: [] }, } as const satisfies StepPermissions; export const DEFAULT_SETTINGS = { version: WORKFLOW_SCHEMA_VERSION, statusShortcut: DEFAULT_STATUS_SHORTCUT, } as const satisfies WorkflowSettings; export type LoadCatalogOptions = { readonly userDirectory?: string; }; export type WorkflowDirectoryEntry = { readonly name: string; readonly isFile: () => boolean; }; export type ConfigFileSystem = { readonly readDirectory: ( path: string, ) => Promise>; readonly readTextFile: (path: string) => Promise; readonly realPath: (path: string) => Promise; }; export type ConfigEnvironment = { readonly getVariable: (name: string) => string | undefined; readonly homeDirectory: () => string; }; export type ConfigLoaderDependencies = { readonly fileSystem: ConfigFileSystem; readonly environment: ConfigEnvironment; }; export type LoadedDirectory = { readonly workflows: ReadonlyArray; readonly diagnostics: ReadonlyArray; }; export type LoadedSettings = { readonly settings: WorkflowSettings; readonly diagnostics: ReadonlyArray; }; export type ConfigLoader = { readonly defaultUserWorkflowDirectory: () => string; readonly loadSettings: (userDirectory: string) => Promise; readonly loadCatalog: ( options: LoadCatalogOptions, ) => Promise; };