import type { ChannelKind, ConnectionInput, ConnectionProtocol } from "#setup/scaffold/index.js"; import { type ProjectResolution } from "./project-resolution.js"; export { CURRENT_DIRECTORY_PROJECT_NAME } from "#setup/scaffold/index.js"; export type { ChannelKind }; /** Where the user wants to chat with the agent after scaffolding finishes. */ export type ChatPreference = "web" | "slack" | "repl" | "api" | "skip"; export type AiGatewayEnvFile = ".env.local" | ".env"; export type ResolvedAiGatewayCredentials = { kind: "unresolved"; } | { kind: "api-key"; envFile: AiGatewayEnvFile; }; /** * The scaffold directory. `inPlace` is the directory decision (scaffold into * the target directory itself vs a `./` child). The target step * resolves the path before the scaffold step writes it. */ export type ResolvedProjectPath = { kind: "unresolved"; inPlace: boolean; } | { kind: "resolved"; inPlace: boolean; path: string; }; /** * The `--headless` Vercel project flags. Read ONLY on the headless path (see * {@link ProvisioningMode}): an interactive run prompts for these decisions * instead, so the args never reach the prompt code. * * Provisioning a Vercel project is the default; `skipVercel` is the explicit * opt-out, and `team`/`project` parameterize it. */ export interface ArgsHeadlessProject { /** Do not create, link, deploy, or inherit credentials from a Vercel project. */ skipVercel?: boolean; /** Vercel team slug (scope). Undefined = the current vercel scope. */ team?: string; /** Existing project slug to link. Undefined = create a new project named after the agent. */ project?: string; } /** * The `--headless` AI Gateway flags, read only on the headless path alongside * {@link ArgsHeadlessProject}. */ export interface ArgsHeadlessAiGateway { /** AI Gateway API key. Undefined = inherit the linked project's gateway (OIDC). */ apiKey?: string; } /** * Provisioning mode for the resolve-provisioning and preflight boxes. The * headless args live ONLY on the `headless: true` variant, so the interactive * path cannot read them: the type makes it unrepresentable. An interactive run * resolves every provisioning decision by prompting. */ export type ProvisioningMode = { headless: true; project: ArgsHeadlessProject; aiGateway: ArgsHeadlessAiGateway; } | { headless: false; }; /** * Resolved project decision, fully concrete. The variant determines whether * the flow creates a project, links an existing one, or avoids Vercel project * mutation entirely. Decided once, up front, by the resolve-provisioning box. */ export type ResolvedVercelProject = { kind: "none"; } | { kind: "new"; project: string; team: string; } | { kind: "existing"; project: string; team: string; }; /** A concrete project to ensure exists and link. */ export type ResolvedVercelProjectSpec = Extract; /** * Resolved gateway-credential decision, independent of the project plan. * `inherit` uses the linked project's OIDC gateway; `byok` writes an AI * Gateway key; `byop` leaves provider credentials outside the AI Gateway path. */ export type ResolvedAiGateway = { kind: "inherit"; } | { kind: "byok"; apiGatewayKey: string; } | { kind: "byop"; }; /** * How the scaffolded agent reaches a model. `"gateway"` routes through the * Vercel AI Gateway (OIDC, or a pasted gateway key). `"self"` scaffolds an * inline provider `byok` block reading `process.env`, derived from the model * picked earlier in the interview. Decided by the resolve-provisioning box. */ export type WiringMode = "gateway" | "self"; /** * How much of the onboarding flow runs. `"complete"` is the full interview and * provisioning sequence; `"one-shot"` scaffolds the base template with the * default model and skips everything after the scaffold (provisioning, model, * channels, linking, credentials, connections, deploy, chat). Decided by the * select-setup-mode box right after the target is resolved. */ export type SetupMode = "complete" | "one-shot"; /** * One fully-specified connection to scaffold. The select-connections box * resolves everything a prompt could decide (slug, protocol, the entry * definition, and how the connector gets provisioned) during the interview, * so the add-connections box runs effects without prompting. */ export interface ConnectionPlan { slug: string; protocol: ConnectionProtocol; entry: ConnectionInput; provision: { kind: "connect"; service: string; } | { kind: "connect-manual"; } | { kind: "command-hint"; service: string; } | { kind: "none"; }; } /** * The in-memory state threaded through every setup box. Greenfield onboarding * populates every field, while in-project setup starts from the detected * on-disk facts and leaves the onboarding-only plans at their defaults. */ export interface SetupState { agentName: string; /** Decided by the select-setup-mode box; gates every post-scaffold box. */ setupMode: SetupMode; modelId: string; modelWiring: WiringMode; /** Channels chosen in the interview phase; scaffolded later by the channels box. */ channelSelection: ChannelKind[]; /** Connections planned in the interview phase; scaffolded later by the connections box. */ connectionSelection: ConnectionPlan[]; /** Decided once by the resolve-provisioning box; executed later by the link box. */ vercelProject: ResolvedVercelProject; /** Decided once by the resolve-provisioning box; executed by the AI Gateway credential box. */ aiGateway: ResolvedAiGateway; projectPath: ResolvedProjectPath; aiGatewayCredentials: ResolvedAiGatewayCredentials; chat: ChatPreference | null; /** Channels scaffolded so far in this run. */ channels: ChannelKind[]; webScaffolded: boolean; slackScaffolded: boolean; deploymentDependenciesInstalled: boolean; /** The linked Vercel project facts, from the link box or the on-disk `.vercel` link. */ project: ProjectResolution; deploymentPending: boolean; slackbotCreated: boolean; slackbotAttached: boolean; slackConnectorUid: string | undefined; /** Deep link that opens a DM compose with the bot ("chat with your agent"). */ slackChatUrl: string | undefined; slackWorkspaceName: string | undefined; } export declare function createDefaultSetupState(): SetupState; /** * Whether this run has a Vercel project available, planned or already linked. * During onboarding the resolve-provisioning box records the plan at the end * of the interview (after channels and connections), so the first disjunct * drives the post-scaffold deploy gating. During in-project setup there is no * plan, but the detected on-disk link resolves `state.project`, so the second * disjunct fires instead. In the create flow `state.project` stays unresolved * until the link box runs, so the second disjunct only fires when a real link * exists. */ export declare function hasVercelProject(state: Pick): boolean; export declare function requireProjectPath(state: Pick): string; /** * Deep-frozen copy of {@link SetupState} for a box's gather and perform faces. * The contract types those faces as `Readonly`; this freeze makes * the guarantee hold at runtime for the nested plan objects too, so the only * state transition is `apply` on the live state. */ export declare function snapshotSetupState(state: SetupState): SetupState;