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.
61 lines • 3.35 kB
TypeScript
import { type DiscordMessage, type GatewayDeps } from './gateway.js';
import { type ProjectTarget, type RunSnapshot } from './routing.js';
/**
* The Discord chatbot (#680): chat to The Framework from Discord instead of the dashboard.
*
* Every effect is an injected function, so the whole bot is testable by handing it a fake
* message — the routing decisions live in `routing.ts` and are pure, and this module is only
* the wiring. Same seam-and-`stop()` shape as the intervention/activity watchers.
*
* Chat history is not written here: a message routed into a run reaches that run's conversation
* through the control channel, and the run commits it to `.the-framework/conversations/` (#908).
* That is the answer to the question #680 asked — the Git repo, via the run that received it.
*/
/**
* How a turn that arrived here is attributed in the committed conversation (#917). Named once,
* here, so the surface owns its own name rather than the daemon spelling it inline twice.
*/
export declare const DISCORD_VIA = "discord";
/** Everything the bot needs from the daemon. */
export interface DiscordBotOptions {
/** The bot token. Distinct from the notification webhook (#627), which cannot read replies. */
token: string;
/** The project a message belongs to when no run is live. */
target: () => Promise<ProjectTarget | undefined>;
/** That project's live run, if it has one. */
liveRun: (projectId: string) => Promise<RunSnapshot | undefined>;
/** Start a new run; resolves the run id, or `undefined` when it could not start. */
start: (projectId: string, text: string) => Promise<string | undefined>;
sendMessage: (projectId: string, text: string, runId: string) => Promise<void>;
sendChoice: (projectId: string, gateId: string, pick: string | string[], runId: string) => Promise<void>;
sendStop: (projectId: string, runId: string) => Promise<void>;
/**
* Bind a run to the channel this message came from (#932), so the session's answers are posted
* back where it was asked. Awaited *before* the run is handed the message: binding first is what
* makes the reply reliably count as new rather than being baselined away.
*/
onRunBound?: (runId: string, channelId: string) => Promise<void>;
/**
* Whether the bot should act, read per message rather than at start, so turning it off takes
* effect without restarting the daemon — the same contract the notification watchers follow.
*/
enabled?: () => Promise<boolean>;
/** Restrict the bot to one channel. Unset means it answers wherever it is addressed. */
channelId?: string | undefined;
fetchImpl?: typeof fetch;
onLog?: (message: string) => void;
/** Gateway seams, for tests. */
gateway?: GatewayDeps;
}
/** A running bot. `stop()` is what takes it offline on `Ctrl+C`. */
export interface DiscordBot {
stop(): void;
/** Handle one message. Exposed so tests drive a cycle without a socket, like the watchers' `poll()`. */
handleMessage(message: DiscordMessage): Promise<void>;
}
/**
* Connect the bot and route messages. Never throws: a chat integration that can take the daemon
* down is worse than one that is quiet.
*/
export declare function startDiscordBot(opts: DiscordBotOptions): DiscordBot;
//# sourceMappingURL=bot.d.ts.map