import { Type, type Static, type TSchema } from '@sinclair/typebox'; import { Value } from '@sinclair/typebox/value'; import type { PreviousCell } from '../play-runtime/cell-staleness'; import type { PlaySandboxRuntimeLimits } from '../play-runtime/sandbox-runtime-limits'; import type { ToolExecuteResult } from '../play-runtime/tool-result-types'; import type { ToolExecutionErrorSchemaVersion } from '../tool-execution-error'; import type { PlayDataset, PlayDatasetInput, PlayDatasetRow } from './dataset'; export const LEGACY_PLAY_AUTHORING_CONTRACT_EDITION = 1 as const; export const PLAY_AUTHORING_CONTRACT_EDITION = 4 as const; export const PLAY_AUTHORING_INPUT_SCHEMA_SNAPSHOT_EDITION = 3 as const; export const SUPPORTED_PLAY_AUTHORING_CONTRACT_EDITIONS = [1, 2, 3, 4] as const; export type PlayAuthoringContractEdition = (typeof SUPPORTED_PLAY_AUTHORING_CONTRACT_EDITIONS)[number]; export const PLAY_AUTHORING_CONTRACT_CHANGELOG = [ { edition: 1, changed: 'Historical extraction semantics. Invalid or unresolved optional authoring fields may normalize to absence.', compatibilityOwner: 'Plays Runtime', newWritesEnd: '2026-11-03', readerRemoval: 'After every retained edition 1 artifact has a materialized admitted snapshot.', }, { edition: 2, changed: 'Pins admitted authoring snapshots and rejects ambiguous billing, secret, webhook, fetch, timeout, receipt-wait, and freshness contracts.', compatibilityOwner: 'Plays Runtime', newWritesEnd: '2026-08-03', readerRemoval: 'After every retained edition 2 artifact has an admitted input schema or has been republished.', }, { edition: 3, changed: 'Materializes the declared input schema in the admitted snapshot so launch never reparses current source.', compatibilityOwner: 'Plays Runtime', newWritesEnd: '2026-08-19', readerRemoval: null, }, { edition: 4, changed: 'ctx.secrets.get(name) resolves an allowed secret to a plaintext string for ordinary Play code. Editions 1–3 retain opaque secret handles.', compatibilityOwner: 'Plays Runtime', newWritesEnd: null, readerRemoval: null, }, ] as const; export const PLAY_AUTHORING_CONTRACT_ISSUE_CODES = [ 'sql_listener_unknown_tool', 'sql_listener_unknown_stream', 'sql_listener_where_unknown_field', 'sql_listener_where_invalid_operator', 'sql_listener_binding_shape', 'invalid_cron_expression', 'invalid_cron_timezone', 'unknown_type_member', 'untyped_monitor_event', 'play_authoring_billing_limit_invalid', 'play_authoring_billing_limit_unresolved', 'play_authoring_webhook_hmac_invalid', 'play_authoring_standard_webhooks_invalid', 'play_authoring_secret_invalid', 'play_authoring_secret_undeclared', 'play_authoring_cron_timezone_invalid', 'play_authoring_tool_request_invalid', 'play_authoring_durable_policy_invalid', 'play_authoring_csv_option_invalid', 'play_authoring_dataset_option_invalid', 'play_authoring_step_option_invalid', 'play_authoring_run_play_option_invalid', 'play_authoring_dynamic_identity_unvalidated', 'play_authoring_fetch_secret_requires_tls', 'play_authoring_fetch_idempotency_required', 'play_authoring_fetch_key_reused_in_loop', 'play_authoring_binding_invalid', 'play_authoring_input_schema_unresolved', 'tool_response_raw_access', 'check_error', 'docflow_compile_error', 'docflow_visualization_missing', 'docflow_binding_drift', 'docflow_branch_labels_required', 'docflow_branch_requires_decision', 'docflow_direction_not_top_down', 'docflow_directive_ignored', 'docflow_dataset_unrepresented', 'docflow_loop_ambiguous', 'docflow_loop_foreign_step', 'docflow_loop_incomplete', 'docflow_dataset_column_undrawn', 'docflow_undrawn_declaration_invalid', 'docflow_layout_complexity', 'docflow_topology_invalid', 'docflow_io_ambiguous', 'docflow_input_not_found', 'docflow_output_not_found', 'docflow_label_counts_rows', 'docflow_node_kind_unsupported', 'docflow_child_play_undrawn', 'docflow_waterfall_region_invalid', ] as const; export type PlayAuthoringContractIssueCode = (typeof PLAY_AUTHORING_CONTRACT_ISSUE_CODES)[number]; export type PlayAuthoringContractIssue = { code: PlayAuthoringContractIssueCode; severity: 'error' | 'warning'; path: string; message: string; hint?: string; }; export type PlaySqlListenerOperation = 'INSERT' | 'UPDATE' | 'DELETE'; export type PlayStandardWebhookHeaderFamily = 'standard' | 'svix'; export type PlayStandardWebhookAuth = { type: 'standard-webhooks'; /** `webhook-*` for Standard Webhooks, `svix-*` for Svix senders. */ headerFamily: PlayStandardWebhookHeaderFamily; /** Deepline Secret names used for ordinary operation and rotation overlap. */ signingSecrets: string[]; /** Replay-protection window. Omitted means the Standard Webhooks 5-minute default. */ toleranceSeconds?: number; }; export const PLAY_SQL_LISTENER_WHERE_OPERATORS = [ 'eq', 'neq', 'in', 'notIn', 'isNull', 'isNotNull', 'ilike', ] as const; export const PLAY_SQL_LISTENER_ID_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]*$/; export const PLAY_SQL_LISTENER_TOOL_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]*\.[a-zA-Z][a-zA-Z0-9_-]*$/; export type PlaySqlListenerFilterScalar = string | number | boolean | null; export type PlaySqlListenerFilterOperator = { eq?: PlaySqlListenerFilterScalar; neq?: PlaySqlListenerFilterScalar; in?: PlaySqlListenerFilterScalar[]; notIn?: PlaySqlListenerFilterScalar[]; isNull?: true; isNotNull?: true; ilike?: string; }; export type PlaySqlListenerWhere = { before?: Record; after?: Record; }; const PLAY_SQL_LISTENER_FIELD_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/; const PLAY_SQL_LISTENER_WHERE_OPERATOR_SET = new Set( PLAY_SQL_LISTENER_WHERE_OPERATORS, ); function isPlaySqlListenerFilterScalar( value: unknown, ): value is PlaySqlListenerFilterScalar { return ( value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' ); } /** Normalize only an already-admitted filter snapshot at the contract seam. */ export function normalizeAdmittedSqlListenerWhere( where: unknown, ): PlaySqlListenerWhere { if (!isPlainRecord(where)) return {}; const normalized: PlaySqlListenerWhere = {}; for (const scope of ['before', 'after'] as const) { const scopeValue = where[scope]; if (!isPlainRecord(scopeValue)) continue; const normalizedScope: Record = {}; for (const [field, operators] of Object.entries(scopeValue)) { if ( !PLAY_SQL_LISTENER_FIELD_PATTERN.test(field) || !isPlainRecord(operators) ) { continue; } const normalizedOperators: PlaySqlListenerFilterOperator = {}; for (const [operator, value] of Object.entries(operators)) { if (!PLAY_SQL_LISTENER_WHERE_OPERATOR_SET.has(operator)) continue; if ( (operator === 'in' || operator === 'notIn') && Array.isArray(value) && value.length > 0 && value.every(isPlaySqlListenerFilterScalar) ) { normalizedOperators[operator] = value; } else if ( (operator === 'isNull' || operator === 'isNotNull') && value === true ) { normalizedOperators[operator] = true; } else if (operator === 'ilike' && typeof value === 'string') { normalizedOperators.ilike = value; } else if ( (operator === 'eq' || operator === 'neq') && isPlaySqlListenerFilterScalar(value) ) { normalizedOperators[operator] = value; } } if (Object.keys(normalizedOperators).length > 0) { normalizedScope[field] = normalizedOperators; } } if (Object.keys(normalizedScope).length > 0) { normalized[scope] = normalizedScope; } } return normalized; } export type PlaySqlListenerDeclaration = { id: string; tool: string; stream: string; operations?: PlaySqlListenerOperation[]; where?: PlaySqlListenerWhere; }; export type PlaySqlListenerEvent> = { tool: string; stream: string; operation: PlaySqlListenerOperation; before: T | null; after: T | null; changedAt: string; metadata: { outboxId: string; listenerId: string; table: string; }; }; export type PlayTriggerSqlListenerSummary = { id: string; tool?: string; stream?: string; operations: string[]; where?: PlaySqlListenerWhere; }; /** Author-facing shape of one SQL-listener invocation. */ export type PlayTriggerSqlListenerEventSummary = { delivery: 'one_event_per_matched_row'; fields: Array< | 'tool' | 'stream' | 'operation' | 'before' | 'after' | 'changedAt' | 'metadata' >; }; export type PlayTriggersSummary = { sqlListeners?: PlayTriggerSqlListenerSummary[]; /** Present when the play has at least one sqlListener trigger. */ sqlListenerEvent?: PlayTriggerSqlListenerEventSummary; cron?: { schedule: string; timezone?: string }; webhook?: true; }; /** Derive the recognized trigger projection from an already-admitted snapshot. */ export function derivePlayTriggersSummary( bindings: PlayAuthoringAstBindings | null | undefined, ): PlayTriggersSummary | null { if (!bindings) return null; const summary: PlayTriggersSummary = {}; if (bindings.sqlListeners && bindings.sqlListeners.length > 0) { summary.sqlListeners = bindings.sqlListeners.map((listener) => ({ id: listener.id, ...(listener.tool ? { tool: listener.tool } : {}), ...(listener.stream ? { stream: listener.stream } : {}), operations: listener.operations, ...(isPlainRecord(listener.where) ? { where: listener.where as PlaySqlListenerWhere } : {}), })); summary.sqlListenerEvent = { delivery: 'one_event_per_matched_row', fields: [ 'tool', 'stream', 'operation', 'before', 'after', 'changedAt', 'metadata', ], }; } if (bindings.cron?.schedule) { summary.cron = bindings.cron.timezone ? { schedule: bindings.cron.schedule, timezone: bindings.cron.timezone } : { schedule: bindings.cron.schedule }; } if (bindings.webhook) summary.webhook = true; return summary.sqlListeners || summary.cron || summary.webhook ? summary : null; } function isPlainRecord(value: unknown): value is Record { return Boolean(value) && typeof value === 'object' && !Array.isArray(value); } /** Internal AST Adapter shape. Invalid legacy fields exist only until admission rejects them. */ export type PlayAuthoringAstSqlListenerDeclaration = { id: string; tool?: string; stream?: string; where?: unknown; monitor?: string; output?: string; table?: `${string}.${string}` | string; operations: string[]; }; export type PlayAuthoringAstBindings = { webhook?: { hmac?: { algorithm?: 'sha256'; header?: string; secretEnv: string; }; auth?: PlayStandardWebhookAuth; }; cron?: { schedule: string; timezone?: string }; sqlListeners?: PlayAuthoringAstSqlListenerDeclaration[]; invalidSqlListenerSingular?: boolean; invalidSqlListenerShape?: boolean; secrets?: string[]; }; /** The one public type for options accepted by definePlay. */ export type PlayAuthoringBindings = { description?: string; compatibility?: { toolErrorSchemaVersion?: ToolExecutionErrorSchemaVersion; /** * Bump only when a Play's response transformation changes serialized tool * output and requires fresh durable tool receipts. */ toolResponseReceiptRevision?: string; }; inline?: boolean; billing?: { maxCreditsPerRun?: number; }; /** * Play-level sandbox settings. The default deadline is 30 minutes; set a * static `timeout` such as `"90m"` or `"2h"` for a bounded batch, up to 4h. * This is distinct from `ctx.tools.execute({ timeoutMs })`, which limits one * provider-call transport rather than the whole Play. */ runtime?: { timeout?: string; size?: 'standard'; }; webhook?: { hmac?: { algorithm?: 'sha256'; header?: string; secretEnv: string; }; auth?: PlayStandardWebhookAuth; }; cron?: { schedule: string; timezone?: string; }; sqlListeners?: PlaySqlListenerDeclaration[]; secrets?: readonly string[]; }; /** The one public type for durable managed-tool calls. */ export type PlayToolExecutionRequest = { id: string; tool: string; input: Record; description?: string; force?: boolean; staleAfterSeconds?: DurableCallStaleAfterSeconds; timeoutMs?: PlayRuntimeTimeoutMs; receiptWaitMs?: PlayReceiptWaitMs; }; export type PlayToolCallOptions = Omit< PlayToolExecutionRequest, 'id' | 'tool' | 'input' >; /** @deprecated Pass a SQL string directly to ctx.customerDb.query. */ export type PlaySqlQuery = { readonly kind: 'sql.query'; readonly text: string; readonly values: readonly unknown[]; }; declare const PLAY_SECRET_HANDLE_BRAND: unique symbol; declare const PLAY_SECRET_PROMISE_BRAND: unique symbol; /** * An opaque reference to a workspace secret used by legacy authoring-contract * editions. New Plays receive plaintext strings from `ctx.secrets.get`. * * @sdkReference runtime 176 SecretHandle */ export type PlaySecretHandle = { readonly [PLAY_SECRET_HANDLE_BRAND]: never; /** Name of the workspace secret, uppercased. Never its value. */ readonly name: string; /** Renders `[secret:NAME]`, so an interpolated handle leaks nothing. */ toString(): string; /** Always throws. A secret handle is deliberately not serializable. */ toJSON(): never; }; /** A secret-reading promise returned only by `ctx.secrets.get`. */ export type PlaySecretPromise = Promise & { readonly [PLAY_SECRET_PROMISE_BRAND]: never; }; /** An opaque legacy secret handle retained for earlier Play artifacts. */ export type PlaySecretValue = PlaySecretHandle; /** * One resolved authentication scheme, built by `ctx.secrets.bearer` or `ctx.secrets.header` and attached to a request through `init.auth`. * * @sdkReference runtime 177 SecretAuth */ export type PlaySecretAuth = { /** `bearer` sends `Authorization: Bearer `; `header` sends a named header. */ readonly kind: 'bearer' | 'header'; /** The value whose bytes the runtime attaches. */ readonly secret: string | PlaySecretPromise | PlaySecretValue; /** Header name, set only when `kind` is `header`. */ readonly header?: string; }; /** One or more resolved authentication schemes for an outbound request. */ export type PlaySecretAuthInput = PlaySecretAuth | readonly PlaySecretAuth[]; /** * The `init` accepted by `ctx.fetch`. Same shape as `RequestInit` plus `auth`. * * @sdkReference runtime 174 SecretAwareRequestInit */ export type PlaySecretAwareRequestInit = Omit & { /** Ordinary request headers, recorded in the durable receipt with any resolved Play secret value redacted. Prefer `auth` for credentials: it enforces HTTPS and keeps the auth header out of the receipt. */ headers?: HeadersInit; /** * One or more credentialed headers for this request. Pass a single `ctx.secrets` auth for the common case, or an array when an API requires multiple credentialed headers — for example, Supabase with both `apikey` and `Authorization`. Auth-helper requests require HTTPS and omit the credential from the durable receipt. Each auth entry must target a distinct header. */ auth?: PlaySecretAuthInput; }; export type PlayLooseObject = { [key: string]: PlayLooseObject }; /** The one public return-object constraint for authored Plays. */ export type PlayReturnObject = Record & { readonly _metadata?: never; }; /** The one public input-contract carrier for object-form Play definitions. */ export type PlayAuthoringInputContract = { readonly schema: Record; readonly __inputType?: TInput; }; /** Shared object-form `definePlay(config)` contract. */ export type PlayAuthoringDefineConfig< TInput, TOutput extends PlayReturnObject, TContext, > = { id: string; description?: string; input: PlayAuthoringInputContract; run: (ctx: TContext, input: TInput) => Promise; bindings?: PlayAuthoringBindings; billing?: PlayAuthoringBindings['billing']; runtime?: PlayAuthoringBindings['runtime']; compatibility?: PlayAuthoringBindings['compatibility']; }; /** Shared callable-plus-handle shape returned by `definePlay`. */ export type PlayAuthoringDefinedPlay< TInput, TOutput extends PlayReturnObject, TContext, THandle extends object, > = ((ctx: TContext, input: TInput) => Promise) & THandle & { readonly bindings?: PlayAuthoringBindings; readonly runtime?: PlayAuthoringBindings['runtime']; readonly compatibility?: PlayAuthoringBindings['compatibility']; readonly playName: string; }; /** Canonical resolver shape for a customer-authored durable step. */ export type PlayAuthoringStepResolver = ( row: Row, ctx: TContext, index: number, previousCell?: PreviousCell, ) => Value | Promise; export type PlayAuthoringDatasetColumnRunInput = { /** Current row, including previously computed columns. */ readonly row: Row; /** Runtime context for tool, Play, fetch, and log calls. */ readonly ctx: TContext; /** Zero-based row index for this dataset run. */ readonly index: number; /** Prior stored cell value and freshness metadata when this cell reruns. */ readonly previousCell?: PreviousCell; }; export type PlayAuthoringDatasetColumnDefinition = { /** Compute one cell value. Receives the previous stored value when rerunning. */ readonly run: ( input: PlayAuthoringDatasetColumnRunInput, ) => Value | Promise; /** Optional row-level gate. Skipped rows produce `null` for this column. */ readonly runIf?: (row: Row, index: number) => boolean | Promise; }; export type PlayAuthoringConditionalStepResolver< Row, Value, TContext, Else = null, > = { readonly kind: 'conditional'; readonly when: (row: Row, index: number) => boolean | Promise; readonly run: PlayAuthoringStepResolver; readonly elseValue: Else; else( value: ValueElse, ): PlayAuthoringConditionalStepResolver; }; export type PlayAuthoringStepOptions = { /** Optional row-level gate. Skipped rows produce `null` for this column. */ readonly runIf?: (row: Row, index: number) => boolean | Promise; /** Legacy dataset-column flag. Prefer freshness on the reusable call. */ readonly recompute?: boolean; /** Legacy error-recompute flag accepted for older authored Plays. */ readonly recomputeOnError?: boolean; /** Legacy cell staleness metadata accepted for older authored Plays. */ readonly staleAfterSeconds?: number; }; /** Explicitly mark a step program as a provider fallback waterfall. */ export type PlayAuthoringStepProgramOptions = { readonly continueOnProviderUnavailable?: boolean; }; export type PlayAuthoringStepProgram< Input, Output, TContext, Return = Output, > = { readonly kind: 'steps'; readonly steps: readonly PlayAuthoringStepProgramStep[]; readonly returnResolver?: PlayAuthoringStepResolver; readonly continueOnProviderUnavailable?: boolean; readonly __inputType?: (input: Input) => void; step( name: Name, resolver: | PlayAuthoringStepResolver | PlayAuthoringConditionalStepResolver | PlayAuthoringStepProgramResolver, ): PlayAuthoringStepProgram< Input, Output & Record, TContext, Return >; step( name: Name, resolver: | PlayAuthoringStepResolver | PlayAuthoringStepProgramResolver, options: PlayAuthoringStepOptions, ): PlayAuthoringStepProgram< Input, Output & Record, TContext, Return >; return( resolver: PlayAuthoringStepResolver, ): PlayAuthoringStepProgram; }; export type PlayAuthoringStepProgramResolver = { readonly kind: 'steps'; readonly steps: readonly PlayAuthoringStepProgramStep[]; readonly returnResolver?: PlayAuthoringStepResolver; readonly __inputType?: (input: Input) => void; }; export type PlayAuthoringRunnableStepProgram = Pick< PlayAuthoringStepProgram, 'kind' | 'steps' | 'returnResolver' >; export type PlayAuthoringStepProgramStep = { readonly name: string; readonly recompute?: boolean; readonly recomputeOnError?: boolean; readonly staleAfterSeconds?: number; readonly resolver: | PlayAuthoringStepResolver, unknown, TContext> | PlayAuthoringConditionalStepResolver< Record, unknown, TContext > | PlayAuthoringStepProgramResolver< Record, unknown, TContext >; }; export type PlayAuthoringColumnResolver = | PlayAuthoringStepResolver | PlayAuthoringConditionalStepResolver | PlayAuthoringRunnableStepProgram; export type PlayAuthoringStepProgramOutput = TProgram extends PlayAuthoringStepProgram< unknown, infer Output, unknown, unknown > ? Output : never; export type PlayAuthoringDatasetRowKey = | (keyof InputRow & string) | readonly (keyof InputRow & string)[] | ((row: InputRow, index: number) => string | number | readonly unknown[]); export type PlayAuthoringDatasetDefinitionOptions = { key?: PlayAuthoringDatasetRowKey; }; export type PlayAuthoringDatasetRunOptions = { description?: string; key?: PlayAuthoringDatasetRowKey; onRowError?: 'isolate' | 'fail'; mode?: 'upsert' | 'net_new'; /** * Computed columns this dataset produces that the authored `@mermaid` * diagram deliberately does not draw. A diagrammed Play must account for * every column it computes: draw it inside the dataset's `subgraph` loop * region, or name it here. Nothing else opts a column out. */ undrawnColumns?: readonly string[]; }; export const PLAY_AUTHORING_DATASET_RUN_OPTION_FIELDS = [ 'description', 'key', 'onRowError', 'mode', 'undrawnColumns', ] as const; export type PlayAuthoringDatasetBuilder< InputRow extends object, OutputRow extends object, TContext, > = { /** Define one output column for every row in this dataset. */ withColumn( name: Name, resolver: PlayAuthoringColumnResolver, ): PlayAuthoringDatasetBuilder< InputRow, OutputRow & Record, TContext >; /** Define a nullable output column with object-form authoring and a row gate. */ withColumn( name: Name, definition: PlayAuthoringDatasetColumnDefinition< OutputRow, Value, TContext > & { readonly runIf: ( row: OutputRow, index: number, ) => boolean | Promise; }, ): PlayAuthoringDatasetBuilder< InputRow, OutputRow & Record, TContext >; /** Define an output column with object-form authoring and typed previous-cell access. */ withColumn( name: Name, definition: PlayAuthoringDatasetColumnDefinition< OutputRow, Value, TContext >, ): PlayAuthoringDatasetBuilder< InputRow, OutputRow & Record, TContext >; /** Define a nullable output column with a resolver and row-level options. */ withColumn( name: Name, resolver: | PlayAuthoringStepResolver | PlayAuthoringRunnableStepProgram, options: PlayAuthoringStepOptions, ): PlayAuthoringDatasetBuilder< InputRow, OutputRow & Record, TContext >; /** Add all columns declared by one reusable step program. */ withColumns< Program extends PlayAuthoringStepProgram< OutputRow, object, TContext, unknown >, >( program: Program, ): PlayAuthoringDatasetBuilder< InputRow, PlayAuthoringStepProgramOutput, TContext >; /** @deprecated Dataset `.step(...)` was replaced by `.withColumn(...)`. */ step( name: Name, resolver: PlayAuthoringColumnResolver, ): never; /** * Execute the row-column program and return a durable dataset handle. * `upsert` preserves row-by-row enrichment. `net_new` admits and returns only * unseen stable keys. `isolate` records failed rows while siblings continue; * `fail` opts into fail-fast behavior. */ run( options?: PlayAuthoringDatasetRunOptions, ): Promise>; }; export type PlayAuthoringReferenceLike = | { readonly playName: string; readonly name?: string } | { readonly name: string; readonly playName?: string }; /** Discoverable, non-deprecated Runtime Context members shown to Play authors. */ export const PLAY_AUTHORING_RUNTIME_CONTEXT_MEMBERS = [ 'csv', 'customerDb', 'dataset', 'fetch', 'log', 'run', 'runPlay', 'runSteps', 'secrets', 'sleep', 'tool', 'tools', ] as const; /** Runtime Context compatibility members that must not be suggested to authors. */ export const PLAY_AUTHORING_RUNTIME_CONTEXT_OMITTED_MEMBERS = [ 'map', 'step', ] as const; /** Discoverable, non-deprecated Dataset Builder members shown to Play authors. */ export const PLAY_AUTHORING_DATASET_BUILDER_MEMBERS = [ 'run', 'withColumn', 'withColumns', ] as const; /** Dataset Builder compatibility members that must not be suggested to authors. */ export const PLAY_AUTHORING_DATASET_BUILDER_OMITTED_MEMBERS = ['step'] as const; export type PlayAuthoringCsvRenameMap = Record< string, string | readonly string[] >; export type PlayAuthoringFileInput = string & { readonly __deeplineFileInputMetadata?: TMetadata; }; export type PlayAuthoringCsvInput< TRow extends object = Record, > = PlayAuthoringFileInput<{ readonly kind: 'csv'; readonly row: TRow; }>; export type PlayAuthoringColumnMap = Partial< Record, string | readonly string[]> >; export type PlayAuthoringCsvOptions = { /** Human-readable description for runtime logs and inspection. */ description?: string; /** Canonical field-to-header aliases. */ columns?: PlayAuthoringCsvRenameMap; /** Header rename map; use `columns` for new code. */ rename?: PlayAuthoringCsvRenameMap; /** Canonical fields required after header normalization. */ required?: readonly string[]; }; export type PlayAuthoringCallExecution = 'inline'; export type PlayAuthoringCallOptions = { description: string; execution?: PlayAuthoringCallExecution; timeoutMs?: never; }; export type PlayAuthoringRuntimeStepOptions = { semanticKey?: string; staleAfterSeconds?: DurableCallStaleAfterSeconds; }; export type PlayAuthoringFetchOptions = { staleAfterSeconds?: DurableCallStaleAfterSeconds; }; /** * The value `ctx.fetch(...)` resolves to: a plain durable record, not a WHATWG `Response`. The body is read once at request time so the call can be checkpointed and replayed, so `bodyText` and `json` are already-materialized properties. There is no `.json()`, `.text()`, or `.body` to await — `await res.json()` is a type error, not a typing problem. * * @sdkReference runtime 175 PlayFetchResponse */ export type PlayAuthoringFetchResponse = { /** True when the response status is in the 2xx range. */ ok: boolean; /** HTTP status code as returned by the upstream server. */ status: number; /** HTTP status text as returned by the upstream server. */ statusText: string; /** Final response URL after any redirects. */ url: string; /** Response headers, lowercased, with any known secret values redacted. */ headers: Record; /** Full response body as text, with any known secret values redacted. */ bodyText: string; /** * The parsed body, eagerly decoded at request time. Read it as a property — `const body = res.json`, never `await res.json()`. Null when the body is empty AND when it is not valid JSON: a malformed payload is reported as null rather than thrown, so check `res.ok` and fall back to `res.bodyText` before treating null as an empty result. */ json: unknown | null; }; export type PlayAuthoringCustomerDbQueryOptions = { maxRows?: number; timeoutMs?: number; }; export type PlayAuthoringRunStepsOptions = { description?: string; }; /** * Stable identity of the currently executing Play invocation. * * The id is unchanged while Deepline resumes or retries the same durable run. * A separately submitted run intentionally receives a new id. */ export type PlayAuthoringRunScope = { readonly id: string; }; /** Factual authoring guidance rendered into SDK and offline-agent references. */ export const PLAY_AUTHORING_DOCUMENTATION = { fetchBatching: { warning: 'A static ctx.fetch key inside a loop is a warning because every iteration must still have distinct method, URL, body, or safe headers. One durable receipt must never stand in for every request.', guidance: 'Keep the static fetch label. For a mutating batch, make the body distinct and use a replay-stable external Idempotency-Key such as `${ctx.run.id}:signals:${batchIndex}`.', }, runId: { semantics: 'ctx.run.id is stable while Deepline retries or resumes one durable run. A separately submitted run receives a new id.', use: 'Use it when deriving an external idempotency key for a sequence of batches.', }, staticCallKeys: { constraint: 'Durable call keys — the ctx.fetch key, the ctx.dataset key, the ctx.step id — must be static string literals. The key names a durable receipt, so check, publish, and replay have to agree on it before the body runs. A key computed at runtime cannot be resolved at check time and is rejected.', consequence: 'This is an architectural constraint, not a style rule. A play cannot loop over a computed key, so it cannot page a large table with a helper like page(pageNumber). Unrolling one literal key per page is not a design at any real page count.', workaround: 'Push the aggregation server-side and call it once: a SQL function, a view, or a provider endpoint that returns the whole result. Keep unrolled literal keys only for a handful of genuinely distinct calls. To fan out over rows, use ctx.dataset with a static key — the per-row receipt identity comes from the row, not from the key.', }, } as const; /** * Shown when a ctx.fetch key is present but not statically resolvable. Named * separately so the field registry, the check hint, and the generated * reference cannot drift from one another. */ export const PLAY_AUTHORING_STATIC_FETCH_KEY_HINT = `${PLAY_AUTHORING_DOCUMENTATION.staticCallKeys.workaround} Do not compute the key.` as const; /** The complete customer-authored `ctx` Interface shared by every Adapter. */ export interface PlayAuthoringRuntimeContext { /** * Load a staged CSV file as a durable dataset handle. * @sdkReference runtime 040 ctx.csv(path, options) */ csv>( path: string | PlayAuthoringCsvInput, options?: PlayAuthoringCsvOptions, ): Promise>; /** * Create a persisted row dataset and define durable output columns. * @sdkReference runtime 060 ctx.dataset(key, items) */ dataset>( key: string, items: TSource, ): PlayAuthoringDatasetBuilder< PlayDatasetRow & object, PlayDatasetRow & object, PlayAuthoringRuntimeContext >; /** @deprecated `ctx.map(...)` was replaced by `ctx.dataset(...)`. */ map>( key: string, items: TSource, options?: PlayAuthoringDatasetDefinitionOptions< PlayDatasetRow & object >, ): never; /** * Identity for this durable run. Use this to build a replay-stable external * idempotency key, for example when posting a sequence of batches. */ readonly run: PlayAuthoringRunScope; tools: { /** * Execute a provider tool through the durable receipt contract. * @sdkReference runtime 150 ctx.tools.execute(request) */ execute( request: PlayToolExecutionRequest, ): Promise>; }; customerDb: { query>( statement: PlaySqlQuery | string, options?: PlayAuthoringCustomerDbQueryOptions, ): Promise; }; /** Shorthand for one managed tool call. */ tool( key: string, toolId: string, input: Record, options?: { description?: string }, ): Promise>; /** * Execute one reusable step program against a scalar input. * @sdkReference runtime 180 ctx.runSteps(program, input, options) */ runSteps, TOutput>( program: PlayAuthoringRunnableStepProgram< TOutput, PlayAuthoringRuntimeContext > & { readonly __inputType?: (input: TInput) => void }, input: TInput, options?: PlayAuthoringRunStepsOptions, ): Promise; /** * Create one scalar durable checkpoint. * @sdkReference runtime 130 ctx.step(id, fn) */ step( id: string, run: () => T | Promise, options?: PlayAuthoringRuntimeStepOptions, ): Promise; /** * Execute a durable, replay-safe HTTP request. * @sdkReference runtime 170 ctx.fetch(key, url, init) */ fetch( key: string, url: string | URL, init?: PlaySecretAwareRequestInit, options?: PlayAuthoringFetchOptions, ): Promise; secrets: { /** * Read an allowed workspace secret inside the running Play; do not log or return it. Declare uppercase names in top-level `secrets`. * * @sdkReference runtime 171 ctx.secrets.get(name) */ get(name: string): PlaySecretPromise; /** * Send a credential as `Authorization: Bearer `. Await `get` first; * its direct promise remains accepted for source compatibility, while other * promises are rejected. * * @sdkReference runtime 172 ctx.secrets.bearer(secret) */ bearer( secret: string | PlaySecretPromise | PlaySecretHandle, ): PlaySecretAuth; /** * Send a credential as a named header, for APIs that do not use bearer * tokens — `x-api-key`, `apikey`, `private-token`, and similar. * * @sdkReference runtime 173 ctx.secrets.header(header, secret) */ header( header: string, secret: string | PlaySecretPromise | PlaySecretHandle, ): PlaySecretAuth; }; /** * Compose another Play inline under a stable call key. * @sdkReference runtime 140 ctx.runPlay(key, playRef, input, options) */ runPlay( key: string, playRef: string | PlayAuthoringReferenceLike, input: Record, options: PlayAuthoringCallOptions, ): Promise; log(message: string): void; sleep(ms: number): Promise; } const SecretEnvironmentNameSchema = Type.String({ pattern: '^[A-Z][A-Z0-9_]{1,63}$', description: 'An uppercase environment variable name beginning with a letter.', }); const SqlListenerFilterScalarSchema = Type.Union([ Type.String(), Type.Number(), Type.Boolean(), Type.Null(), ]); export const PLAY_AUTHORING_FIELD_REGISTRY = { description: { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'Enrich a company.', invalid: '', absent: undefined, unresolved: { expression: 'description' }, edition1: 'Legacy play.', }, referenceType: 'string', required: false, resolution: 'static-required', issueCode: 'play_authoring_binding_invalid', description: 'Optional non-empty human-readable summary of the Play.', errorMessage: 'description must be a non-empty static string.', }, 'compatibility.toolErrorSchemaVersion': { schema: Type.Union([Type.Literal(0), Type.Literal(1)]), fixtures: { valid: 1, invalid: 2, absent: undefined, unresolved: { expression: 'version' }, edition1: 0, }, referenceType: '0 | 1', required: false, resolution: 'static-required', issueCode: 'play_authoring_binding_invalid', description: 'Artifact-pinned tool error behavior, either 0 or 1.', errorMessage: 'compatibility.toolErrorSchemaVersion must be the static literal 0 or 1.', }, 'compatibility.toolResponseReceiptRevision': { schema: Type.String({ minLength: 1, maxLength: 64, pattern: '^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$', }), fixtures: { valid: 'raw-v2-receipt-v1', invalid: 'has spaces', absent: undefined, unresolved: { expression: 'revision' }, edition1: undefined, }, referenceType: 'string', required: false, resolution: 'static-required', issueCode: 'play_authoring_binding_invalid', description: 'Explicit durable-receipt revision for a response transformation; bump only when serialized tool output changes.', errorMessage: 'compatibility.toolResponseReceiptRevision must be a non-empty static identifier using letters, numbers, dots, underscores, or hyphens.', }, inline: { schema: Type.Boolean(), fixtures: { valid: true, invalid: 'true', absent: undefined, unresolved: { expression: 'inline' }, edition1: false, }, referenceType: 'boolean', required: false, resolution: 'static-required', issueCode: 'play_authoring_binding_invalid', description: 'Compiler hint for an inline named Play handler.', errorMessage: 'inline must be a static boolean.', }, 'billing.maxCreditsPerRun': { schema: Type.Number({ exclusiveMinimum: 0 }), fixtures: { valid: 1, invalid: 0, absent: undefined, unresolved: { expression: 'cap' }, edition1: 1, }, referenceType: 'number', required: false, resolution: 'static-required', issueCode: 'play_authoring_billing_limit_invalid', description: 'Maximum Deepline credits permitted for one Play Run.', errorMessage: 'billing.maxCreditsPerRun must be a static number greater than 0. Remove it for no run cap.', }, 'bindings.webhook.hmac.secretEnv': { schema: SecretEnvironmentNameSchema, fixtures: { valid: 'WEBHOOK_SECRET', invalid: 'webhook_secret', absent: undefined, unresolved: { expression: 'secretEnv' }, edition1: 'WEBHOOK_SECRET', }, referenceType: 'string', required: true, resolution: 'static-required', issueCode: 'play_authoring_webhook_hmac_invalid', description: 'Environment variable containing the webhook HMAC secret.', errorMessage: 'bindings.webhook.hmac.secretEnv must be an uppercase environment variable name beginning with a letter.', }, 'bindings.webhook.hmac.algorithm': { schema: Type.Literal('sha256'), fixtures: { valid: 'sha256', invalid: 'sha1', absent: undefined, unresolved: { expression: 'algorithm' }, edition1: 'sha256', }, referenceType: "'sha256'", required: false, resolution: 'static-required', issueCode: 'play_authoring_webhook_hmac_invalid', description: 'Webhook signature hash algorithm. Only sha256 is supported.', errorMessage: 'bindings.webhook.hmac.algorithm must be the static literal "sha256".', }, 'bindings.webhook.hmac.header': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'x-signature', invalid: '', absent: undefined, unresolved: { expression: 'header' }, edition1: 'x-signature', }, referenceType: 'string', required: false, resolution: 'static-required', issueCode: 'play_authoring_webhook_hmac_invalid', description: 'HTTP header containing the webhook signature.', errorMessage: 'bindings.webhook.hmac.header must be a non-empty static string.', }, 'bindings.webhook.auth.type': { schema: Type.Literal('standard-webhooks'), fixtures: { valid: 'standard-webhooks', invalid: 'svix', absent: undefined, unresolved: { expression: 'type' }, edition1: undefined, }, referenceType: "'standard-webhooks'", // auth itself is optional; once present, the AST adapter requires this // field together with headerFamily and signingSecrets. required: false, resolution: 'static-required', issueCode: 'play_authoring_standard_webhooks_invalid', description: 'Uses the Standard Webhooks v1 symmetric signing scheme.', errorMessage: 'bindings.webhook.auth.type must be the static literal "standard-webhooks".', }, 'bindings.webhook.auth.headerFamily': { schema: Type.Union([Type.Literal('standard'), Type.Literal('svix')]), fixtures: { valid: 'svix', invalid: 'webhook', absent: undefined, unresolved: { expression: 'headerFamily' }, edition1: undefined, }, referenceType: "'standard' | 'svix'", // auth itself is optional; once present, the AST adapter requires this // field together with type and signingSecrets. required: false, resolution: 'static-required', issueCode: 'play_authoring_standard_webhooks_invalid', description: 'Header namespace expected from the webhook provider.', errorMessage: 'bindings.webhook.auth.headerFamily must be the static literal "standard" or "svix".', }, 'bindings.webhook.auth.signingSecrets[]': { schema: SecretEnvironmentNameSchema, fixtures: { valid: 'VECTOR_WEBHOOK_SECRET', invalid: 'vector_webhook_secret', absent: undefined, unresolved: { expression: 'secret' }, edition1: undefined, }, referenceType: 'string', // auth itself is optional; once present, the AST adapter requires this // field together with type and headerFamily. required: false, resolution: 'static-required', issueCode: 'play_authoring_standard_webhooks_invalid', description: 'Deepline Secret name used to verify Standard Webhooks.', errorMessage: 'bindings.webhook.auth.signingSecrets entries must be uppercase Deepline Secret names beginning with a letter.', }, 'bindings.webhook.auth.toleranceSeconds': { schema: Type.Integer({ minimum: 1, maximum: 3600 }), fixtures: { valid: 300, invalid: 0, absent: undefined, unresolved: { expression: 'toleranceSeconds' }, edition1: undefined, }, referenceType: 'number', required: false, resolution: 'static-required', issueCode: 'play_authoring_standard_webhooks_invalid', description: 'Accepted delivery timestamp skew in seconds, from 1 through 3600.', errorMessage: 'bindings.webhook.auth.toleranceSeconds must be a static whole number from 1 through 3600.', }, 'bindings.cron.schedule': { schema: Type.String({ minLength: 1 }), fixtures: { valid: '0 9 * * *', invalid: '', absent: undefined, unresolved: { expression: 'schedule' }, edition1: '0 9 * * *', }, referenceType: 'string', required: true, resolution: 'static-required', issueCode: 'play_authoring_binding_invalid', description: 'Five-field cron expression.', errorMessage: 'bindings.cron.schedule must be a non-empty static string.', }, 'bindings.cron.timezone': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'UTC', invalid: '', absent: undefined, unresolved: { expression: 'timezone' }, edition1: 'UTC', }, referenceType: 'string', required: false, resolution: 'static-required', issueCode: 'play_authoring_cron_timezone_invalid', description: 'IANA timezone. Omitted means UTC.', errorMessage: 'bindings.cron.timezone must be a valid non-empty IANA timezone string.', }, 'bindings.sqlListeners': { schema: Type.Array(Type.Object({}, { additionalProperties: true })), fixtures: { valid: [], invalid: 'listeners', absent: undefined, unresolved: { expression: 'listeners' }, edition1: [], }, referenceType: 'SqlListener[]', required: false, resolution: 'static-required', issueCode: 'play_authoring_binding_invalid', description: 'Static provider-monitor listener declarations.', errorMessage: 'bindings.sqlListeners must be a static array of objects.', }, 'bindings.sqlListeners[].id': { schema: Type.String({ pattern: PLAY_SQL_LISTENER_ID_PATTERN.source }), fixtures: { valid: 'job-openings', invalid: '1-job-openings', absent: undefined, unresolved: { expression: 'listenerId' }, edition1: 'job-openings', }, referenceType: 'string', required: true, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Unique static listener identifier within one Play.', errorMessage: 'bindings.sqlListeners[].id must begin with a letter and contain only letters, numbers, underscores, or hyphens.', }, 'bindings.sqlListeners[].tool': { schema: Type.String({ pattern: PLAY_SQL_LISTENER_TOOL_PATTERN.source }), fixtures: { valid: 'deepline_native.company_radar', invalid: 'company_radar', absent: undefined, unresolved: { expression: 'toolId' }, edition1: 'deepline_native.company_radar', }, referenceType: 'string', required: true, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Modeled provider monitor tool id in provider.tool form.', errorMessage: 'bindings.sqlListeners[].tool must use static provider.tool syntax.', }, 'bindings.sqlListeners[].stream': { schema: Type.String({ pattern: PLAY_SQL_LISTENER_ID_PATTERN.source }), fixtures: { valid: 'company_job_openings', invalid: '1-company-job-openings', absent: undefined, unresolved: { expression: 'stream' }, edition1: 'company_job_openings', }, referenceType: 'string', required: true, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Static output stream key exposed by the monitor tool.', errorMessage: 'bindings.sqlListeners[].stream must be a static stream identifier.', }, 'bindings.sqlListeners[].operations[]': { schema: Type.Union([ Type.Literal('INSERT'), Type.Literal('UPDATE'), Type.Literal('DELETE'), ]), fixtures: { valid: 'INSERT', invalid: 'UPSERT', absent: undefined, unresolved: { expression: 'operation' }, edition1: 'UPDATE', }, referenceType: "'INSERT' | 'UPDATE' | 'DELETE'", required: false, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Database operation that wakes the listener.', errorMessage: 'bindings.sqlListeners[].operations entries must be INSERT, UPDATE, or DELETE.', }, 'bindings.sqlListeners[].where.before': { schema: Type.Record(Type.String(), Type.Unknown()), fixtures: { valid: { status: { eq: 'open' } }, invalid: 'status=open', absent: undefined, unresolved: 'beforeFilter', edition1: { status: { eq: 'open' } }, }, referenceType: 'Record', required: false, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Column filters evaluated against the row before mutation.', errorMessage: 'bindings.sqlListeners[].where.before must be a static object keyed by column.', }, 'bindings.sqlListeners[].where.after': { schema: Type.Record(Type.String(), Type.Unknown()), fixtures: { valid: { status: { eq: 'open' } }, invalid: 'status=open', absent: undefined, unresolved: 'afterFilter', edition1: { status: { eq: 'open' } }, }, referenceType: 'Record', required: false, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Column filters evaluated against the row after mutation.', errorMessage: 'bindings.sqlListeners[].where.after must be a static object keyed by column.', }, 'bindings.sqlListeners[].where.*.*.eq': { schema: SqlListenerFilterScalarSchema, fixtures: { valid: 'open', invalid: { nested: true }, absent: undefined, unresolved: { expression: 'equalsValue' }, edition1: 'open', }, referenceType: 'SqlListenerFilterScalar', required: false, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Scalar equality condition.', errorMessage: 'SQL listener eq must compare a scalar value.', }, 'bindings.sqlListeners[].where.*.*.neq': { schema: SqlListenerFilterScalarSchema, fixtures: { valid: 'closed', invalid: { nested: true }, absent: undefined, unresolved: { expression: 'notEqualsValue' }, edition1: 'closed', }, referenceType: 'SqlListenerFilterScalar', required: false, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Scalar inequality condition.', errorMessage: 'SQL listener neq must compare a scalar value.', }, 'bindings.sqlListeners[].where.*.*.in': { schema: Type.Array(SqlListenerFilterScalarSchema, { minItems: 1 }), fixtures: { valid: ['open', 'pending'], invalid: [], absent: undefined, unresolved: { expression: 'acceptedValues' }, edition1: ['open'], }, referenceType: 'readonly SqlListenerFilterScalar[]', required: false, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Non-empty scalar membership condition.', errorMessage: 'SQL listener in must contain at least one scalar value.', }, 'bindings.sqlListeners[].where.*.*.notIn': { schema: Type.Array(SqlListenerFilterScalarSchema, { minItems: 1 }), fixtures: { valid: ['closed'], invalid: [], absent: undefined, unresolved: { expression: 'rejectedValues' }, edition1: ['closed'], }, referenceType: 'readonly SqlListenerFilterScalar[]', required: false, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Non-empty scalar exclusion condition.', errorMessage: 'SQL listener notIn must contain at least one scalar value.', }, 'bindings.sqlListeners[].where.*.*.isNull': { schema: Type.Literal(true), fixtures: { valid: true, invalid: false, absent: undefined, unresolved: { expression: 'isNull' }, edition1: true, }, referenceType: 'true', required: false, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Matches null values when set to true.', errorMessage: 'SQL listener isNull must be the static literal true.', }, 'bindings.sqlListeners[].where.*.*.isNotNull': { schema: Type.Literal(true), fixtures: { valid: true, invalid: false, absent: undefined, unresolved: { expression: 'isNotNull' }, edition1: true, }, referenceType: 'true', required: false, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Matches non-null values when set to true.', errorMessage: 'SQL listener isNotNull must be the static literal true.', }, 'bindings.sqlListeners[].where.*.*.ilike': { schema: Type.String(), fixtures: { valid: '%software%', invalid: 1, absent: undefined, unresolved: { expression: 'pattern' }, edition1: '%software%', }, referenceType: 'string', required: false, resolution: 'static-required', issueCode: 'sql_listener_binding_shape', description: 'Case-insensitive SQL pattern condition.', errorMessage: 'SQL listener ilike must be a string pattern.', }, 'bindings.secrets[]': { schema: SecretEnvironmentNameSchema, fixtures: { valid: 'API_TOKEN', invalid: 'api_token', absent: undefined, unresolved: { expression: 'secret' }, edition1: 'API_TOKEN', }, referenceType: 'string', required: false, resolution: 'static-required', issueCode: 'play_authoring_secret_invalid', description: 'Environment variable made available to the Play.', errorMessage: 'bindings.secrets entries must be uppercase environment variable names beginning with a letter.', }, 'ctx.tools.execute.staleAfterSeconds': { schema: Type.Union([Type.Null(), Type.Integer({ minimum: 0 })]), fixtures: { valid: 0, invalid: -1, absent: undefined, unresolved: { expression: 'ttl' }, edition1: null, }, referenceType: 'number | null', required: false, resolution: 'runtime-allowed', issueCode: 'play_authoring_durable_policy_invalid', description: '`0` always executes; `null`/omitted never expires; a positive integer is a TTL in seconds.', errorMessage: 'staleAfterSeconds must be null, 0 (always execute), or a positive whole number of seconds.', }, 'ctx.tools.execute.id': { schema: Type.String({ pattern: '\\S' }), fixtures: { valid: 'company-enrichment', invalid: '', absent: undefined, unresolved: { expression: 'receiptId' }, edition1: 'company-enrichment', }, referenceType: 'string', required: true, resolution: 'runtime-allowed', issueCode: 'play_authoring_tool_request_invalid', description: 'Stable durable receipt identity within one execution scope.', errorMessage: 'ctx.tools.execute id must be a non-empty string.', }, 'ctx.tools.execute.tool': { schema: Type.String({ pattern: '\\S' }), fixtures: { valid: 'openmart_enrich_company', invalid: '', absent: undefined, unresolved: { expression: 'toolId' }, edition1: 'openmart_enrich_company', }, referenceType: 'K', required: true, resolution: 'runtime-allowed', issueCode: 'play_authoring_tool_request_invalid', description: 'Integration tool id resolved against the generated ToolMap.', errorMessage: 'ctx.tools.execute tool must be a non-empty tool id.', }, 'ctx.tools.execute.input': { schema: Type.Record(Type.String(), Type.Unknown()), fixtures: { valid: { domain: 'example.com' }, invalid: 'example.com', absent: undefined, unresolved: 'toolInput', edition1: {}, }, referenceType: "K extends keyof ToolMap ? ToolMap[K]['input'] : Record", required: true, resolution: 'runtime-allowed', issueCode: 'play_authoring_tool_request_invalid', description: 'Tool-specific input object.', errorMessage: 'ctx.tools.execute input must be an object.', }, 'ctx.tools.execute.description': { schema: Type.String(), fixtures: { valid: 'Enrich the company.', invalid: 1, absent: undefined, unresolved: { expression: 'description' }, edition1: 'Enrich the company.', }, referenceType: 'string', required: false, resolution: 'runtime-allowed', issueCode: 'play_authoring_tool_request_invalid', description: 'Human-readable purpose of the durable tool call.', errorMessage: 'ctx.tools.execute description must be a string.', }, 'ctx.tools.execute.force': { schema: Type.Boolean(), fixtures: { valid: true, invalid: 'true', absent: undefined, unresolved: { expression: 'force' }, edition1: false, }, referenceType: 'boolean', required: false, resolution: 'runtime-allowed', issueCode: 'play_authoring_tool_request_invalid', description: 'Explicitly bypasses a completed durable tool receipt.', errorMessage: 'ctx.tools.execute force must be a boolean.', }, 'ctx.tools.execute.timeoutMs': { schema: Type.Integer({ minimum: 1 }), fixtures: { valid: 1, invalid: 0, absent: undefined, unresolved: { expression: 'timeout' }, edition1: 1, }, referenceType: 'number', required: false, resolution: 'runtime-allowed', issueCode: 'play_authoring_durable_policy_invalid', description: 'Positive whole-number runtime transport timeout in milliseconds.', errorMessage: 'timeoutMs must be a positive whole number of milliseconds.', }, 'ctx.tools.execute.receiptWaitMs': { schema: Type.Integer({ minimum: 1 }), fixtures: { valid: 1, invalid: 0, absent: undefined, unresolved: { expression: 'receiptWait' }, edition1: 1, }, referenceType: 'number', required: false, resolution: 'runtime-allowed', issueCode: 'play_authoring_durable_policy_invalid', description: 'Positive whole-number durable receipt wait budget in milliseconds.', errorMessage: 'receiptWaitMs must be a positive whole number of milliseconds.', }, 'ctx.csv.options.description': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'Load account rows.', invalid: '', absent: undefined, unresolved: { expression: 'description' }, edition1: 'Load rows.', }, referenceType: 'string', required: false, resolution: 'static-when-present', issueCode: 'play_authoring_csv_option_invalid', description: 'Non-empty description for a staged CSV load.', errorMessage: 'ctx.csv options.description must be non-empty.', }, 'ctx.csv.options.columns': { schema: Type.Record( Type.String(), Type.Union([ Type.String({ minLength: 1 }), Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }), ]), ), fixtures: { valid: { domain: ['domain', 'Company Domain'] }, invalid: { domain: [] }, absent: undefined, unresolved: { expression: { dynamic: true } }, edition1: { domain: 'domain' }, }, referenceType: 'CsvRenameMap', required: false, resolution: 'runtime-allowed', issueCode: 'play_authoring_csv_option_invalid', description: 'Canonical field-to-header aliases for a staged CSV.', errorMessage: 'ctx.csv options.columns values must be a non-empty header or alias list.', }, 'ctx.csv.options.rename': { schema: Type.Record( Type.String(), Type.Union([ Type.String({ minLength: 1 }), Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }), ]), ), fixtures: { valid: { domain: 'Company Domain' }, invalid: { domain: '' }, absent: undefined, unresolved: { expression: { dynamic: true } }, edition1: { domain: 'domain' }, }, referenceType: 'CsvRenameMap', required: false, resolution: 'static-when-present', issueCode: 'play_authoring_csv_option_invalid', description: 'Legacy header rename aliases for a staged CSV.', errorMessage: 'ctx.csv options.rename values must be a non-empty header or alias list.', }, 'ctx.csv.options.required': { schema: Type.Array(Type.String({ minLength: 1 })), fixtures: { valid: ['domain'], invalid: [''], absent: undefined, unresolved: { expression: 'requiredColumns' }, edition1: [], }, referenceType: 'readonly string[]', required: false, resolution: 'static-when-present', issueCode: 'play_authoring_csv_option_invalid', description: 'Canonical columns required after CSV normalization.', errorMessage: 'ctx.csv options.required entries must be non-empty column names.', }, 'ctx.dataset.key': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'accounts', invalid: '', absent: undefined, unresolved: { expression: 'datasetKey' }, edition1: 'rows', }, referenceType: 'string', required: true, resolution: 'static-required', issueCode: 'play_authoring_dataset_option_invalid', description: 'Stable durable identity for one dataset.', errorMessage: 'ctx.dataset key must be a non-empty static string.', }, 'ctx.dataset.run.description': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'Enrich account rows.', invalid: '', absent: undefined, unresolved: { expression: 'description' }, edition1: 'Process rows.', }, referenceType: 'string', required: false, resolution: 'static-when-present', issueCode: 'play_authoring_dataset_option_invalid', description: 'Non-empty description for one dataset execution.', errorMessage: 'ctx.dataset run description must be non-empty.', }, 'ctx.dataset.run.key': { schema: Type.Union([ Type.String({ minLength: 1 }), Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }), Type.Function( [Type.Record(Type.String(), Type.Unknown()), Type.Integer()], Type.Union([ Type.String(), Type.Number(), Type.Readonly(Type.Array(Type.Unknown())), ]), ), ]), fixtures: { valid: (row: Record) => String(row.domain), invalid: [], absent: undefined, unresolved: { expression: 'rowKey' }, edition1: ['domain'], }, referenceType: 'DatasetRowKey', required: false, resolution: 'runtime-dynamic', issueCode: 'play_authoring_dataset_option_invalid', description: 'Stable field or fields used for durable row identity.', errorMessage: 'ctx.dataset run key must be a non-empty field, field list, or key function.', }, 'ctx.dataset.run.onRowError': { schema: Type.Union([Type.Literal('isolate'), Type.Literal('fail')]), fixtures: { valid: 'isolate', invalid: 'continue', absent: undefined, unresolved: { expression: 'rowErrorPolicy' }, edition1: 'fail', }, referenceType: "'isolate' | 'fail'", required: false, resolution: 'static-when-present', issueCode: 'play_authoring_dataset_option_invalid', description: 'Whether row failures isolate or fail the whole dataset.', errorMessage: 'ctx.dataset run onRowError must be "isolate" or "fail".', }, 'ctx.dataset.run.mode': { schema: Type.Union([Type.Literal('upsert'), Type.Literal('net_new')]), fixtures: { valid: 'upsert', invalid: 'append', absent: undefined, unresolved: { expression: 'datasetMode' }, edition1: 'upsert', }, referenceType: "'upsert' | 'net_new'", required: false, resolution: 'static-when-present', issueCode: 'play_authoring_dataset_option_invalid', description: 'Whether the dataset returns all rows or only newly admitted rows.', errorMessage: 'ctx.dataset run mode must be "upsert" or "net_new".', }, 'ctx.dataset.run.undrawnColumns': { schema: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }), fixtures: { valid: ['miss_reason'], invalid: [], absent: undefined, unresolved: { expression: 'undrawnColumns' }, edition1: ['miss_reason'], }, referenceType: 'readonly string[]', required: false, resolution: 'static-when-present', issueCode: 'play_authoring_dataset_option_invalid', description: 'Computed columns deliberately left out of the authored @mermaid diagram.', errorMessage: 'ctx.dataset run undrawnColumns must be a non-empty array of static column-name strings.', }, 'ctx.step.id': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'load-settings', invalid: '', absent: undefined, unresolved: { expression: 'stepId' }, edition1: 'step', }, referenceType: 'string', required: true, resolution: 'static-required', issueCode: 'play_authoring_step_option_invalid', description: 'Stable durable identity for one scalar checkpoint.', errorMessage: 'ctx.step id must be a non-empty static string.', }, 'ctx.step.semanticKey': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'account:stripe.com', invalid: '', absent: undefined, unresolved: { expression: 'semanticKey' }, edition1: 'account', }, referenceType: 'string', required: false, resolution: 'runtime-dynamic', issueCode: 'play_authoring_step_option_invalid', description: 'Optional semantic receipt identity for a scalar checkpoint.', errorMessage: 'ctx.step semanticKey must be a non-empty string.', }, 'ctx.step.staleAfterSeconds': { schema: Type.Union([Type.Null(), Type.Integer({ minimum: 0 })]), fixtures: { valid: 0, invalid: -1, absent: undefined, unresolved: { expression: 'ttl' }, edition1: null, }, referenceType: 'number | null', required: false, resolution: 'runtime-dynamic', issueCode: 'play_authoring_durable_policy_invalid', description: 'Checkpoint freshness: null/omitted never expires, 0 always executes.', errorMessage: 'staleAfterSeconds must be null, 0 (always execute), or a positive whole number of seconds.', }, 'ctx.fetch.key': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'notify-crm', invalid: '', absent: undefined, unresolved: { expression: 'fetchKey' }, edition1: 'fetch', }, referenceType: 'string', required: true, resolution: 'static-required', issueCode: 'play_authoring_durable_policy_invalid', description: 'Stable durable identity for one external HTTP request.', errorMessage: 'ctx.fetch key must be a non-empty static string.', unresolvedHint: PLAY_AUTHORING_STATIC_FETCH_KEY_HINT, }, 'ctx.fetch.staleAfterSeconds': { schema: Type.Union([Type.Null(), Type.Integer({ minimum: 0 })]), fixtures: { valid: null, invalid: 1.5, absent: undefined, unresolved: { expression: 'ttl' }, edition1: 0, }, referenceType: 'number | null', required: false, resolution: 'runtime-dynamic', issueCode: 'play_authoring_durable_policy_invalid', description: 'Fetch freshness: null/omitted never expires, 0 always executes.', errorMessage: 'staleAfterSeconds must be null, 0 (always execute), or a positive whole number of seconds.', }, 'ctx.runPlay.key': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'enrich-company', invalid: '', absent: undefined, unresolved: { expression: 'callKey' }, edition1: 'child', }, referenceType: 'string', required: true, resolution: 'runtime-dynamic', issueCode: 'play_authoring_run_play_option_invalid', description: 'Stable identity for one inline child Play call.', errorMessage: 'ctx.runPlay key must be a non-empty string.', }, 'ctx.runPlay.playRef': { schema: Type.Union([ Type.String({ minLength: 1 }), Type.Union([ Type.Object( { playName: Type.String({ minLength: 1 }) }, { additionalProperties: true }, ), Type.Object( { name: Type.String({ minLength: 1 }) }, { additionalProperties: true }, ), ]), ]), fixtures: { valid: 'prebuilt/company-lookup', invalid: {}, absent: undefined, unresolved: { expression: 'playRef' }, edition1: { name: 'company-lookup' }, }, referenceType: 'string | PlayReferenceLike', required: true, resolution: 'runtime-dynamic', issueCode: 'play_authoring_run_play_option_invalid', description: 'Child Play name or typed Play definition handle.', errorMessage: 'ctx.runPlay playRef must be a non-empty Play name or definition handle.', }, 'ctx.runPlay.input': { schema: Type.Record(Type.String(), Type.Unknown()), fixtures: { valid: { domain: 'example.com' }, invalid: 'example.com', absent: undefined, unresolved: 'childInput', edition1: {}, }, referenceType: 'Record', required: true, resolution: 'runtime-dynamic', issueCode: 'play_authoring_run_play_option_invalid', description: 'Scalar input object submitted to the child Play.', errorMessage: 'ctx.runPlay input must be an object.', }, 'ctx.runPlay.options.description': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'Enrich the company.', invalid: '', absent: undefined, unresolved: { expression: 'description' }, edition1: 'Run child.', }, referenceType: 'string', required: true, resolution: 'runtime-dynamic', issueCode: 'play_authoring_run_play_option_invalid', description: 'Non-empty purpose for one inline child Play call.', errorMessage: 'ctx.runPlay options.description must be non-empty.', }, 'ctx.runPlay.options.execution': { schema: Type.Literal('inline'), fixtures: { valid: 'inline', invalid: 'child-workflow', absent: undefined, unresolved: { expression: 'execution' }, edition1: 'inline', }, referenceType: "'inline'", required: false, resolution: 'static-required', issueCode: 'play_authoring_run_play_option_invalid', description: 'Child composition strategy. Only inline is supported.', errorMessage: 'ctx.runPlay execution must be "inline".', }, 'ctx.runPlay.options.timeoutMs': { schema: Type.Undefined(), fixtures: { valid: undefined, invalid: 1000, absent: undefined, unresolved: { expression: 'timeoutMs' }, edition1: undefined, }, referenceType: 'never', required: false, resolution: 'unsupported', issueCode: 'play_authoring_run_play_option_invalid', description: 'Unsupported legacy child-workflow timeout.', errorMessage: 'ctx.runPlay timeoutMs is unsupported because child Plays execute inline.', }, 'runtime.timeout': { schema: Type.String({ pattern: '^\\d+\\s*[mh]$' }), fixtures: { valid: '90m', invalid: '90s', absent: undefined, unresolved: { expression: 'timeout' }, edition1: '90m', }, referenceType: 'string', required: false, resolution: 'static-required', issueCode: 'play_authoring_binding_invalid', description: 'Play-level sandbox deadline. The default is 30m; use a static duration such as 90m or 2h, up to 4h.', errorMessage: 'runtime.timeout must be a static duration such as "90m" or "2h".', }, 'runtime.size': { schema: Type.Literal('standard'), fixtures: { valid: 'standard', invalid: 'large', absent: undefined, unresolved: { expression: 'size' }, edition1: 'standard', }, referenceType: "'standard'", required: false, resolution: 'static-required', issueCode: 'play_authoring_binding_invalid', description: 'Deepline-managed sandbox size. Only standard is supported.', errorMessage: 'runtime.size must be the static literal "standard".', }, 'ctx.customerDb.query.statement': { schema: Type.Union([ Type.String({ minLength: 1 }), Type.Object( { kind: Type.Optional(Type.Literal('sql.query')), text: Type.String({ minLength: 1 }), values: Type.Optional(Type.Array(Type.Unknown(), { maxItems: 0 })), }, { additionalProperties: false }, ), ]), fixtures: { valid: { kind: 'sql.query', text: 'select 1', values: [] }, invalid: { kind: 'sql.query', text: 'select $1', values: [1] }, absent: undefined, unresolved: { expression: 'statement' }, edition1: 'select 1', }, referenceType: 'SqlQuery', required: true, resolution: 'runtime-dynamic', issueCode: 'play_authoring_tool_request_invalid', description: 'One non-empty Customer DB SQL string; the deprecated SqlQuery object is accepted only without parameter values.', errorMessage: 'ctx.customerDb.query statement must be a non-empty SQL string. Deprecated SqlQuery objects cannot contain parameter values.', }, 'ctx.customerDb.query.options.maxRows': { schema: Type.Integer({ minimum: 1 }), fixtures: { valid: 100, invalid: 0, absent: undefined, unresolved: { expression: 'maxRows' }, edition1: 100, }, referenceType: 'number', required: false, resolution: 'runtime-dynamic', issueCode: 'play_authoring_tool_request_invalid', description: 'Positive whole-number Customer DB response row limit.', errorMessage: 'ctx.customerDb.query options.maxRows must be a positive whole number.', }, 'ctx.customerDb.query.options.timeoutMs': { schema: Type.Integer({ minimum: 1 }), fixtures: { valid: 1000, invalid: 0, absent: undefined, unresolved: { expression: 'timeoutMs' }, edition1: 1000, }, referenceType: 'number', required: false, resolution: 'runtime-dynamic', issueCode: 'play_authoring_durable_policy_invalid', description: 'Positive whole-number Customer DB timeout in milliseconds.', errorMessage: 'ctx.customerDb.query options.timeoutMs must be a positive whole number of milliseconds.', }, 'ctx.tool.key': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'company', invalid: '', absent: undefined, unresolved: { expression: 'key' }, edition1: 'tool', }, referenceType: 'string', required: true, resolution: 'runtime-dynamic', issueCode: 'play_authoring_tool_request_invalid', description: 'Stable receipt identity for the tool shorthand.', errorMessage: 'ctx.tool key must be a non-empty string.', }, 'ctx.tool.tool': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'openmart_enrich_company', invalid: '', absent: undefined, unresolved: { expression: 'tool' }, edition1: 'openmart_enrich_company', }, referenceType: 'string', required: true, resolution: 'runtime-dynamic', issueCode: 'play_authoring_tool_request_invalid', description: 'Integration tool id for the tool shorthand.', errorMessage: 'ctx.tool tool must be a non-empty tool id.', }, 'ctx.tool.input': { schema: Type.Record(Type.String(), Type.Unknown()), fixtures: { valid: { domain: 'example.com' }, invalid: 'example.com', absent: undefined, unresolved: 'toolInput', edition1: {}, }, referenceType: 'Record', required: true, resolution: 'runtime-dynamic', issueCode: 'play_authoring_tool_request_invalid', description: 'Tool-specific input object for the shorthand.', errorMessage: 'ctx.tool input must be an object.', }, 'ctx.tool.options.description': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'Enrich the company.', invalid: '', absent: undefined, unresolved: { expression: 'description' }, edition1: 'Run tool.', }, referenceType: 'string', required: false, resolution: 'runtime-dynamic', issueCode: 'play_authoring_tool_request_invalid', description: 'Non-empty purpose for the tool shorthand.', errorMessage: 'ctx.tool options.description must be non-empty.', }, 'ctx.runSteps.options.description': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'Score the account.', invalid: '', absent: undefined, unresolved: { expression: 'description' }, edition1: 'Run steps.', }, referenceType: 'string', required: false, resolution: 'runtime-dynamic', issueCode: 'play_authoring_step_option_invalid', description: 'Non-empty purpose for a reusable step program.', errorMessage: 'ctx.runSteps options.description must be non-empty.', }, 'ctx.sleep.ms': { schema: Type.Integer({ minimum: 0 }), fixtures: { valid: 0, invalid: -1, absent: undefined, unresolved: { expression: 'delayMs' }, edition1: 1000, }, referenceType: 'number', required: true, resolution: 'runtime-dynamic', issueCode: 'play_authoring_step_option_invalid', description: 'Non-negative whole-number sleep duration in milliseconds.', errorMessage: 'ctx.sleep ms must be a non-negative whole number.', }, 'ctx.fetch.url': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'https://example.com', invalid: '', absent: undefined, unresolved: { expression: 'url' }, edition1: 'https://example.com', }, referenceType: 'string', required: true, resolution: 'runtime-allowed', issueCode: 'play_authoring_fetch_secret_requires_tls', description: 'HTTP request URL. Secret authentication requires HTTPS.', errorMessage: 'ctx.fetch URL must be a non-empty URL string.', }, 'ctx.fetch.init.method': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'GET', invalid: '', absent: undefined, unresolved: { expression: 'method' }, edition1: 'GET', }, referenceType: 'string', required: false, resolution: 'runtime-allowed', issueCode: 'play_authoring_fetch_idempotency_required', description: 'HTTP method. Mutating methods require an Idempotency-Key.', errorMessage: 'ctx.fetch method must be a non-empty string.', }, 'ctx.fetch.init.headers.Idempotency-Key': { schema: Type.String({ minLength: 1 }), fixtures: { valid: 'contact-123-update', invalid: '', absent: undefined, unresolved: { expression: 'idempotencyKey' }, edition1: 'contact-123-update', }, referenceType: 'string', required: false, resolution: 'runtime-allowed', issueCode: 'play_authoring_fetch_idempotency_required', description: 'Required for mutating HTTP methods to make replay safe.', errorMessage: 'Idempotency-Key must be a non-empty string when provided.', }, } as const satisfies Record< string, { schema: TSchema; fixtures: { valid: unknown; invalid: unknown; absent: undefined; unresolved: unknown; edition1: unknown; }; referenceType: string; required: boolean; resolution: | 'static-required' | 'static-when-present' | 'runtime-allowed' | 'runtime-dynamic' | 'unsupported'; issueCode: PlayAuthoringContractIssueCode; description: string; errorMessage: string; /** * Hint shown when the value is present but could not be resolved * statically. Defaults to generic "use a literal" advice, which is useless * when the author has a real reason to compute the value. Set this on any * field where the reason is common enough to name the way out. */ unresolvedHint?: string; } >; export type PlayAuthoringFieldPath = keyof typeof PLAY_AUTHORING_FIELD_REGISTRY; export type PlayAuthoringBindingsSnapshot = PlayAuthoringAstBindings; export type AdmittedPlayAuthoringContract = { edition: PlayAuthoringContractEdition; staticPipeline: unknown; /** Input schema materialized at admission; launch must never reparse source. */ inputSchema: Record | null; bindings: PlayAuthoringBindingsSnapshot | null; billingLimit: { maxCreditsPerRun: number } | null; runtimeLimit: PlaySandboxRuntimeLimits; allowedSecrets: string[]; }; export class UnsupportedPlayAuthoringContractEditionError extends Error { constructor(value: unknown) { super( `Unsupported Play authoring contract edition ${String(value)}. Supported editions: ${SUPPORTED_PLAY_AUTHORING_CONTRACT_EDITIONS.join(', ')}.`, ); this.name = 'UnsupportedPlayAuthoringContractEditionError'; } } export class PlayAuthoringContractViolationError extends Error { constructor( readonly path: string, readonly code: PlayAuthoringContractIssueCode, readonly detail: string, readonly hint?: string, ) { super(`[${code} path=${path}] ${detail}`); this.name = 'PlayAuthoringContractViolationError'; } } /** A schema violation for one field declared by the Authoring Contract Module. */ export class PlayAuthoringFieldValidationError extends PlayAuthoringContractViolationError { constructor( path: PlayAuthoringFieldPath, code: PlayAuthoringContractIssueCode, message: string, ) { super(path, code, message); this.name = 'PlayAuthoringFieldValidationError'; } } export function normalizePlayAuthoringContractEdition( value: unknown, ): PlayAuthoringContractEdition { const edition = value ?? LEGACY_PLAY_AUTHORING_CONTRACT_EDITION; if ( !SUPPORTED_PLAY_AUTHORING_CONTRACT_EDITIONS.includes( edition as PlayAuthoringContractEdition, ) ) { throw new UnsupportedPlayAuthoringContractEditionError(edition); } return edition as PlayAuthoringContractEdition; } export function validatePlayAuthoringField( path: PlayAuthoringFieldPath, value: unknown, ): void { const definition = PLAY_AUTHORING_FIELD_REGISTRY[path]; if (Value.Check(definition.schema, value)) return; throw new PlayAuthoringFieldValidationError( path, definition.issueCode, definition.errorMessage, ); } export function validateOptionalPlayAuthoringField( path: PlayAuthoringFieldPath, value: unknown, ): void { const definition = PLAY_AUTHORING_FIELD_REGISTRY[path]; if (value === undefined && !definition.required) return; validatePlayAuthoringField(path, value); } /** Normalize the deprecated SqlQuery compatibility shape at the contract seam. */ export function normalizePlayAuthoringCustomerDbStatement( statement: PlaySqlQuery | string, ): string { validatePlayAuthoringField('ctx.customerDb.query.statement', statement); return typeof statement === 'string' ? statement : statement.text; } export type PlayAuthoringBillingLimit = Static< (typeof PLAY_AUTHORING_FIELD_REGISTRY)['billing.maxCreditsPerRun']['schema'] >; export type DurableCallStaleAfterSeconds = Static< (typeof PLAY_AUTHORING_FIELD_REGISTRY)['ctx.tools.execute.staleAfterSeconds']['schema'] >; /** Runtime validation, not TypeScript, enforces positive whole milliseconds. */ export type PlayRuntimeTimeoutMs = number; export type PlayReceiptWaitMs = number; function cloudReferenceType(path: PlayAuthoringFieldPath): string { return PLAY_AUTHORING_FIELD_REGISTRY[path].referenceType; } const DEFAULT_UNRESOLVED_HINT = 'Use a literal value so check, publish, and runtime agree.'; /** * Hint for a field that is present but not statically resolvable. * * The registry is `as const satisfies`, so an entry without `unresolvedHint` * has no such property in its literal type. Read it through here rather than * widening the registry and losing the per-field schema narrowing. */ export function playAuthoringUnresolvedHint( path: PlayAuthoringFieldPath, ): string { const definition = PLAY_AUTHORING_FIELD_REGISTRY[path] as { unresolvedHint?: string; }; return definition.unresolvedHint ?? DEFAULT_UNRESOLVED_HINT; } /** Ambient declarations generated into the cloud Play compiler from this model. */ export const PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [ `export type DurableCallStaleAfterSeconds = ${cloudReferenceType('ctx.tools.execute.staleAfterSeconds')};`, `export type PlayRuntimeTimeoutMs = ${cloudReferenceType('ctx.tools.execute.timeoutMs')};`, `export type PlayReceiptWaitMs = ${cloudReferenceType('ctx.tools.execute.receiptWaitMs')};`, `export type SqlListenerOperation = ${cloudReferenceType('bindings.sqlListeners[].operations[]')};`, 'export type SqlListenerFilterScalar = string | number | boolean | null;', `export type SqlListenerFilterOperator = { eq?: ${cloudReferenceType('bindings.sqlListeners[].where.*.*.eq')}; neq?: ${cloudReferenceType('bindings.sqlListeners[].where.*.*.neq')}; in?: ${cloudReferenceType('bindings.sqlListeners[].where.*.*.in')}; notIn?: ${cloudReferenceType('bindings.sqlListeners[].where.*.*.notIn')}; isNull?: ${cloudReferenceType('bindings.sqlListeners[].where.*.*.isNull')}; isNotNull?: ${cloudReferenceType('bindings.sqlListeners[].where.*.*.isNotNull')}; ilike?: ${cloudReferenceType('bindings.sqlListeners[].where.*.*.ilike')} };`, `export type SqlListenerWhere = { before?: ${cloudReferenceType('bindings.sqlListeners[].where.before')}; after?: ${cloudReferenceType('bindings.sqlListeners[].where.after')} };`, `export type SqlListenerDeclaration = { id: ${cloudReferenceType('bindings.sqlListeners[].id')}; tool: ${cloudReferenceType('bindings.sqlListeners[].tool')}; stream: ${cloudReferenceType('bindings.sqlListeners[].stream')}; operations?: readonly SqlListenerOperation[]; where?: SqlListenerWhere };`, 'export type SqlListenerEvent> = { tool: string; stream: string; operation: SqlListenerOperation; before: T | null; after: T | null; changedAt: string; metadata: { outboxId: string; listenerId: string; table: string } };', "export type SqlQuery = string | { kind?: 'sql.query'; text: string; values?: readonly unknown[] };", 'export type ToolExecutionRequest = {', ` readonly id: ${cloudReferenceType('ctx.tools.execute.id')};`, ` readonly tool: ${cloudReferenceType('ctx.tools.execute.tool')};`, ` readonly input: ${cloudReferenceType('ctx.tools.execute.input')};`, ` readonly description?: ${cloudReferenceType('ctx.tools.execute.description')};`, ` readonly force?: ${cloudReferenceType('ctx.tools.execute.force')};`, ' readonly staleAfterSeconds?: DurableCallStaleAfterSeconds;', ' readonly timeoutMs?: PlayRuntimeTimeoutMs;', ' readonly receiptWaitMs?: PlayReceiptWaitMs;', '};', 'export type PlayBindings = {', ` description?: ${cloudReferenceType('description')};`, ` compatibility?: { toolErrorSchemaVersion?: ${cloudReferenceType('compatibility.toolErrorSchemaVersion')}; toolResponseReceiptRevision?: ${cloudReferenceType('compatibility.toolResponseReceiptRevision')} };`, ` inline?: ${cloudReferenceType('inline')};`, ` billing?: { maxCreditsPerRun?: ${cloudReferenceType('billing.maxCreditsPerRun')} };`, ` runtime?: { timeout?: ${cloudReferenceType('runtime.timeout')}; size?: ${cloudReferenceType('runtime.size')} };`, ` webhook?: { hmac?: { algorithm?: ${cloudReferenceType('bindings.webhook.hmac.algorithm')}; header?: ${cloudReferenceType('bindings.webhook.hmac.header')}; secretEnv: ${cloudReferenceType('bindings.webhook.hmac.secretEnv')} }; auth?: { type: ${cloudReferenceType('bindings.webhook.auth.type')}; headerFamily: ${cloudReferenceType('bindings.webhook.auth.headerFamily')}; signingSecrets: readonly ${cloudReferenceType('bindings.webhook.auth.signingSecrets[]')}[]; toleranceSeconds?: ${cloudReferenceType('bindings.webhook.auth.toleranceSeconds')} } };`, ` cron?: { schedule: ${cloudReferenceType('bindings.cron.schedule')}; timezone?: ${cloudReferenceType('bindings.cron.timezone')} };`, ' sqlListeners?: readonly SqlListenerDeclaration[];', ` secrets?: readonly ${cloudReferenceType('bindings.secrets[]')}[];`, '};', 'declare const SECRET_HANDLE_BRAND: unique symbol;', 'declare const SECRET_PROMISE_BRAND: unique symbol;', 'export type SecretHandle = { readonly [SECRET_HANDLE_BRAND]: never; readonly name: string; toString(): string; toJSON(): never };', 'export type SecretPromise = Promise & { readonly [SECRET_PROMISE_BRAND]: never };', 'export type SecretValue = SecretHandle;', "export type SecretAuth = { readonly kind: 'bearer' | 'header'; readonly secret: string | SecretPromise | SecretHandle; readonly header?: string };", 'export type SecretAuthInput = SecretAuth | readonly SecretAuth[];', 'export type PlayInputContract = { readonly schema: Record; readonly __inputType?: TInput };', 'export type PlayReturnObject = Record & { readonly _metadata?: never };', 'export type CsvRenameMap = Record;', 'export type FileInput = string & { readonly __deeplineFileInputMetadata?: TMetadata };', "export type CsvInput> = FileInput<{ readonly kind: 'csv'; readonly row: TRow }>;", 'export type ColumnMap = { [K in keyof TRow & string]?: string | readonly string[] };', `export type CsvOptions = { description?: ${cloudReferenceType('ctx.csv.options.description')}; columns?: ${cloudReferenceType('ctx.csv.options.columns')}; rename?: ${cloudReferenceType('ctx.csv.options.rename')}; required?: ${cloudReferenceType('ctx.csv.options.required')} };`, "export type PlayCallExecution = 'inline';", `export type PlayCallOptions = { description: ${cloudReferenceType('ctx.runPlay.options.description')}; execution?: ${cloudReferenceType('ctx.runPlay.options.execution')}; timeoutMs?: ${cloudReferenceType('ctx.runPlay.options.timeoutMs')} };`, `export type RuntimeStepOptions = { semanticKey?: ${cloudReferenceType('ctx.step.semanticKey')}; staleAfterSeconds?: ${cloudReferenceType('ctx.step.staleAfterSeconds')} };`, `export type FetchOptions = { staleAfterSeconds?: ${cloudReferenceType('ctx.fetch.staleAfterSeconds')} };`, 'export type PlayFetchResponse = { ok: boolean; status: number; statusText: string; url: string; headers: Record; bodyText: string; json: unknown | null };', 'export type StepResolver = (row: Row, ctx: DeeplinePlayRuntimeContext, index: number, previousCell?: PreviousCell) => Value | Promise;', 'export type DatasetColumnRunInput = { readonly row: Row; readonly ctx: DeeplinePlayRuntimeContext; readonly index: number; readonly previousCell?: PreviousCell };', 'export type DatasetColumnDefinition = { readonly run: (input: DatasetColumnRunInput) => Value | Promise; readonly runIf?: (row: Row, index: number) => boolean | Promise };', "export type ConditionalStepResolver = { readonly kind: 'conditional'; readonly when: (row: Row, index: number) => boolean | Promise; readonly run: StepResolver; readonly elseValue: Else; else(value: ValueElse): ConditionalStepResolver; };", 'export type StepOptions = { readonly runIf?: (row: Row, index: number) => boolean | Promise; readonly recompute?: boolean; readonly recomputeOnError?: boolean; readonly staleAfterSeconds?: number };', 'export type StepProgramOptions = { readonly continueOnProviderUnavailable?: boolean };', "export type StepProgram = { readonly kind: 'steps'; readonly steps: readonly PlayStepProgramStep[]; readonly returnResolver?: StepResolver; readonly continueOnProviderUnavailable?: boolean; readonly __inputType?: (input: Input) => void; step(name: Name, resolver: StepResolver | ConditionalStepResolver | StepProgramResolver): StepProgram, Return>; step(name: Name, resolver: StepResolver | StepProgramResolver, options: StepOptions): StepProgram, Return>; return(resolver: StepResolver): StepProgram; };", "export type StepProgramResolver = { readonly kind: 'steps'; readonly steps: readonly PlayStepProgramStep[]; readonly returnResolver?: StepResolver; readonly __inputType?: (input: Input) => void };", "export type RunnableStepProgram = Pick, 'kind' | 'steps' | 'returnResolver' | '__inputType'>;", "export type RunnableColumnStepProgram = Pick, 'kind' | 'steps' | 'returnResolver'>;", 'export type PlayStepProgramStep = { readonly name: string; readonly recompute?: boolean; readonly recomputeOnError?: boolean; readonly staleAfterSeconds?: number; readonly resolver: StepResolver, unknown> | ConditionalStepResolver, unknown> | StepProgramResolver, unknown> };', 'export type ColumnResolver = StepResolver | ConditionalStepResolver | RunnableColumnStepProgram;', 'export type StepProgramOutput = TProgram extends StepProgram ? Output : never;', 'export type DatasetRowKey = (keyof InputRow & string) | readonly (keyof InputRow & string)[] | ((row: InputRow, index: number) => string | number | readonly unknown[]);', 'export type DatasetDefinitionOptions = { key?: DatasetRowKey };', `export type DatasetRunOptions = { description?: ${cloudReferenceType('ctx.dataset.run.description')}; key?: DatasetRowKey; onRowError?: ${cloudReferenceType('ctx.dataset.run.onRowError')}; mode?: ${cloudReferenceType('ctx.dataset.run.mode')}; undrawnColumns?: ${cloudReferenceType('ctx.dataset.run.undrawnColumns')} };`, 'export type DatasetBuilder = {', ' withColumn(name: Name, resolver: ColumnResolver): DatasetBuilder>;', ' withColumn(name: Name, definition: DatasetColumnDefinition & { readonly runIf: (row: OutputRow, index: number) => boolean | Promise }): DatasetBuilder>;', ' withColumn(name: Name, definition: DatasetColumnDefinition): DatasetBuilder>;', ' withColumn(name: Name, resolver: StepResolver | RunnableColumnStepProgram, options: StepOptions): DatasetBuilder>;', ' withColumns>(program: Program): DatasetBuilder>;', ' step(name: Name, resolver: ColumnResolver): never;', ' run(options?: DatasetRunOptions): Promise>;', '};', 'export type PlayReferenceLike = { readonly playName: string; readonly name?: string } | { readonly name: string; readonly playName?: string };', 'export interface DeeplinePlayRuntimeContext {', ' csv>(path: string | CsvInput, options?: CsvOptions): Promise>;', ' dataset>(key: string, items: TSource): DatasetBuilder & object, PlayDatasetRow & object>;', ' map>(key: string, items: TSource, options?: DatasetDefinitionOptions & object>): never;', ' readonly run: { readonly id: string };', ` runSteps, TOutput>(program: RunnableStepProgram, input: TInput, options?: { description?: ${cloudReferenceType('ctx.runSteps.options.description')} }): Promise;`, ' tools: { execute(request: ToolExecutionRequest): Promise> };', ` customerDb: { query = Record>(statement: SqlQuery, options?: { maxRows?: ${cloudReferenceType('ctx.customerDb.query.options.maxRows')}; timeoutMs?: ${cloudReferenceType('ctx.customerDb.query.options.timeoutMs')} }): Promise };`, ` tool(key: ${cloudReferenceType('ctx.tool.key')}, toolId: K, input: ${cloudReferenceType('ctx.tool.input')}, options?: { description?: ${cloudReferenceType('ctx.tool.options.description')} }): Promise>;`, ' step(id: string, run: () => T | Promise, options?: RuntimeStepOptions): Promise;', " fetch(key: string, url: string | URL, init?: RequestInit & { headers?: HeadersInit & { 'Idempotency-Key'?: string; 'X-Idempotency-Key'?: string }; auth?: SecretAuthInput }, options?: FetchOptions): Promise;", ' secrets: { get(name: string): SecretPromise; bearer(secret: string | SecretPromise | SecretHandle): SecretAuth; header(header: string, secret: string | SecretPromise | SecretHandle): SecretAuth };', ` runPlay(key: string, playRef: ${cloudReferenceType('ctx.runPlay.playRef')}, input: ${cloudReferenceType('ctx.runPlay.input')}, options: PlayCallOptions): Promise;`, ' log(message: string): void;', ` sleep(ms: ${cloudReferenceType('ctx.sleep.ms')}): Promise;`, '}', "export type DefinePlayConfig = { id: string; description?: string; input: PlayInputContract; run: (ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise; bindings?: PlayBindings; billing?: PlayBindings['billing']; runtime?: PlayBindings['runtime']; compatibility?: PlayBindings['compatibility'] };", "export type DefinedPlay = ((ctx: DeeplinePlayRuntimeContext, input: TInput) => Promise) & { readonly name: string; readonly __inputType?: TInput; readonly __outputType?: TOutput; readonly runtime?: PlayBindings['runtime']; readonly compatibility?: PlayBindings['compatibility'] };", ] as const;