UNPKG

framework

Version:

The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.

202 lines 10.7 kB
import { type ParsedAwaitGate } from './turn-gate.js'; import { type ChoicePick, type ChoiceRequest, type FrameworkEvent } from './events.js'; import type { DriverSession, DriverTurn } from './driver/index.js'; import type { AgentMessages } from './agent-messages.js'; /** What a resolved gate yields. */ export interface GateAnswer { /** The label(s) picked, as the continuation prompt words it to the agent. */ answer: string; /** The user picked an option the agent marked `stop` (#358): hand back rather than continue. */ stop: boolean; } /** * Resolve one parsed await gate (#337) to the user's answer text, ready to seed the continuation * prompt: emits the `choice`, parks for the pick (or the headless/abort fallback), and maps the * picked id(s) back to label(s). Round 0 keeps a stable gate id; later rounds get a unique one so * a dashboard never confuses a re-ask with the answer it just resolved. Shared by the build's * `agentAwaitGate`, the direct prompt path, and the backlog loop (#323). * * One path, where there were four. A gate that takes several picks answers with the labels it got * (or `(none)`); every other gate answers with the one label picked. What used to distinguish an * approval or a browser hand-off from an ordinary question was the options the agent wrote, and * that is all it is again. * * The answer also carries whether it *ends* the session, which is read off the option the user * picked rather than off the gate: on a multi-select one stopping pick among several is still a * stop, because an answer that says "stop" is not softened by the answers next to it. */ export declare function resolveAwaitGate(gate: ParsedAwaitGate, round: number, deps: { requestChoice?: ((req: ChoiceRequest) => Promise<ChoicePick>) | undefined; emit: (event: FrameworkEvent) => void; signal?: AbortSignal | undefined; }): Promise<GateAnswer>; /** What {@link runAwaitRounds} hands back. */ export interface AwaitRoundsResult { /** The last turn's text. */ text: string; /** The agent was still asking when the round cap ran out. */ exhausted: boolean; /** The user answered a gate with a `stop` option (#358), so the session ends here. */ stopped: boolean; } /** Inputs to {@link runAwaitRounds}. */ export interface AwaitRoundsOptions { session: DriverSession; /** The prompt that opens the exchange. */ prompt: string; /** Emit the signals each turn carries (#563). */ emitTurnSignals: (text: string) => void; requestChoice?: ((req: ChoiceRequest) => Promise<ChoicePick>) | undefined; emit: (event: FrameworkEvent) => void; signal?: AbortSignal | undefined; /** * Live chat (#714): once the agent stops asking, take the user's own messages, * each resuming the same session. Unset for a headless agent, which then * ends when the agent stops asking — byte-identical to before this existed. */ messages?: AgentMessages | undefined; /** * Keep the chat parked for the next message instead of ending on an idle queue (#1390). * Only for an agent whose own terminal dashboard is the single surface — everything else * ends itself and is reopened via `--resume`. Default off. */ stayOpenChat?: boolean | undefined; /** * Resume a prior session on the OPENING prompt (#720): when the driver session was * seeded with a finished agent's id, this makes the first message `--resume` that * conversation (full prior context) instead of starting fresh. Default off. */ resume?: boolean | undefined; } /** The shared deps of a turn that may hit an await gate or a chat message. */ export interface AwaitTurnDeps { requestChoice?: ((req: ChoiceRequest) => Promise<ChoicePick>) | undefined; emit: (event: FrameworkEvent) => void; emitTurnSignals: (text: string) => void; signal?: AbortSignal | undefined; } /** * Resolve the await gates (#337) a turn ended on: pick the answer, continue with it, repeat until * the agent stops asking, an answer says to stop (#358), or the {@link MAX_AWAIT_ROUNDS} cap trips. * Returns the settled turn plus which of those ended it. * * Every path that runs gates shares this loop: the opening prompt, each chat message, and * the build's `agentAwaitGate`. They differ only in how a turn is continued — a raw * `session.prompt`, or a pass that carries its own continuation — which is what * `continueWith` is. Keeping one loop is what stopped the per-turn signal emission from * having to be added to each copy by hand (#563). */ export declare function drainGates<T extends { text: string; }>(turn: T, deps: AwaitTurnDeps, continueWith: (question: string, answer: string) => Promise<T>): Promise<{ turn: T; exhausted: boolean; stopped: boolean; }>; /** * The live-chat loop (#714): deliver each of the user's messages by resuming the same * session (full conversational context), then honor any await gate it produced. Shared by * the direct prompt path and the build path, which both reach it once their work has settled. * * How it ends is the session-lifecycle rule of #1390. By default the loop only *drains*: a * message that already arrived is processed, and once the queue is idle the session ends * itself rather than parking on the user — merge fires at that natural end (armed + gated), * and a follow-up reopens the conversation via `--resume` (#762), like Claude Code web. * `stayOpen` keeps the old park-for-the-next-message lifecycle, for an agent whose own terminal * dashboard is the only surface — it has no daemon to resume through, so ending would leave * its composer a dead end; that agent still ends on Stop / budget cap (next -> undefined). * * Reports the settled `exhausted` of the *last* chat turn (#742): entering chat means the * opening prompt's await-round cap is no longer the agent's end reason, and a phase that ends * on Stop / close is not exhausted at all. */ export declare function runChatPhase(session: DriverSession, messages: AgentMessages, seed: DriverTurn, deps: AwaitTurnDeps, stayOpen?: boolean): Promise<{ turn: DriverTurn; exhausted: boolean; stopped: boolean; }>; /** * Prompt the agent and honor its await gates (#337) until it stops asking: resolve each gate to * the user's answer, re-prompt with it, and repeat up to {@link MAX_AWAIT_ROUNDS}. When a live-chat * {@link AwaitRoundsOptions.messages} source is wired, the agent then stays open for the user's own * messages (#714) rather than finishing. * * Every turn here is a turn like any other, so each one's signals are emitted. That is the * point of sharing this: the direct prompt path and the backlog loop each had their own copy * of these rounds, and the emission had to be added to each by hand (#563). */ export declare function runAwaitRounds(opts: AwaitRoundsOptions): Promise<AwaitRoundsResult>; /** One option of a {@link requestChoices} single-select gate (#304). */ export interface ChoicesOption { /** Stable id returned when this option is picked. */ id: string; /** The label shown next to the option. */ label: string; /** Optional one-line detail under the label. */ detail?: string; } /** Inputs to {@link requestChoices}. */ export interface ChoicesDeps { /** Stable id for the gate; the pick is posted back against it. */ id: string; /** The prompt shown above the options. */ title: string; /** The options to choose between (pick one). */ options: readonly ChoicesOption[]; /** The option id pre-selected (autopilot auto-accepts it) and used as the headless/abort fallback. Default = the first option. */ recommended?: string; /** The markdown file under approval; the dashboard's doc sidebar renders it. */ file?: string; /** The interactive handler (the CLI wires it to the dashboard); omit for a headless agent. */ requestChoice?: (req: ChoiceRequest) => Promise<ChoicePick>; /** Emit the `choice` / `choice-resolved` events onto the agent stream. */ emit: (event: FrameworkEvent) => void; /** The agent signal; a gate parked for a pick unblocks (to the recommended option) if the agent aborts. */ signal?: AbortSignal; } /** * The single-select gate (#304): show the options with the recommended one * pre-selected, pause, and resolve to the *one* option id the user picked. The twin * of {@link requestMultiSelect} for "pick one" — the agent-facing `showChoices()` * from the built-in system (#326) prompt and the [Research] preset (#331) both build on it. * A headless agent (no `requestChoice`), or one aborted mid-await, falls back to the * recommended option without hanging, so a programmatic agent stays deterministic. */ export declare function requestChoices(deps: ChoicesDeps): Promise<string>; /** One option of a {@link requestMultiSelect} checklist (#332). */ export interface MultiSelectOption { /** Stable id returned when this option is checked. */ id: string; /** The label shown next to the checkbox. */ label: string; /** Optional one-line detail under the label. */ detail?: string; /** Whether this option starts checked (e.g. a low-rated problem in the [Research] preset #331). */ default?: boolean; } /** Inputs to {@link requestMultiSelect}. */ export interface MultiSelectDeps { /** Stable id for the gate; the pick is posted back against it. */ id: string; /** The prompt shown above the checklist. */ title: string; /** The options to choose from (checkboxes). */ options: readonly MultiSelectOption[]; /** The interactive handler (the CLI wires it to the dashboard); omit for a headless agent. */ requestChoice?: (req: ChoiceRequest) => Promise<ChoicePick>; /** Emit the `choice` / `choice-resolved` events onto the agent stream. */ emit: (event: FrameworkEvent) => void; /** The agent signal; a gate parked for a pick unblocks (to the defaults) if the agent aborts. */ signal?: AbortSignal; } /** * The multi-select gate (#332): show a checklist with the default-checked options * pre-selected, pause, and resolve to the *subset* of option ids the user kept * checked. Built on the same `choice` gate + POST-back resolver as the single-select * plan-approval gate (#304), just in checklist mode. A headless agent (no * `requestChoice`) auto-accepts the default set without pausing, so a programmatic * run stays deterministic. This is the primitive the [Research] preset (#331) uses * to let the user pick which problems to deep-dive. */ export declare function requestMultiSelect(deps: MultiSelectDeps): Promise<string[]>; //# sourceMappingURL=await-gate.d.ts.map