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.
69 lines • 3.37 kB
TypeScript
/**
* The daemon's one background clock (E4).
*
* Every sweep used to own a timer: the CI watch on a minute, the worktree sweep and auto PM on ten,
* the session committer's debounce on thirty seconds, the two Discord watchers on a minute each.
* Six intervals with six `setInterval`s, six unref calls and six overlap guards, and no single
* place to look when the answer to "why is nothing happening" is that a sweep is not running.
*
* One interval fires, and each job says how often it wants a turn. A job's cadence stops being a
* duration it has to reason about and becomes a small integer: how many ticks between turns.
*
* The base interval is the finest cadence anything needs. Anything slower is that many ticks —
* exact by construction, because the ratios are integers rather than two timers drifting apart.
*/
/** One thing the daemon does on a schedule. */
export interface TickJob {
/** For the log line when it throws. */
name: string;
/**
* Ticks between turns. `1` is every tick, `20` is every twentieth. A tick is the interval
* coming round, not a turn that ran, so a slow job on the same clock cannot stretch this out.
*
* Its own turn is *skipped* rather than queued, so a slow job never accumulates a backlog of
* missed turns to work through.
*/
every?: number;
/** Run one turn. Awaited, so a long job holds the tick rather than overlapping the next one. */
run: () => Promise<void>;
/**
* Run on the very first tick, which fires at start-up rather than one interval later.
*
* On by default, because the case most of these exist for is a machine that was off while
* something happened. A job that only makes sense once the daemon has been up a while says so.
*/
onStart?: boolean;
}
/** The running clock. */
export interface DaemonTick {
/** Run one tick now, awaiting it. The daemon's shutdown and the tests drive it through this. */
tick: () => Promise<void>;
/**
* Stop the clock, and resolve when the turn already in flight has finished. Awaiting it is how
* a shutdown knows the sweeps have let go of the repo — clearing the interval only stops the
* *next* turn, and these jobs commit and push.
*/
stop: () => Promise<void>;
}
/** How often the clock fires. The finest cadence any job asks for; everything else is a multiple. */
export declare const DAEMON_TICK_MS = 30000;
/** What {@link startDaemonTick} needs. */
export interface DaemonTickOptions {
jobs: readonly TickJob[];
/** Override the base interval (tests). */
intervalMs?: number;
log: (message: string) => void;
}
/**
* Start the clock and return the handle that stops it.
*
* A job that throws costs its own turn and nothing else: the others in the same tick still run,
* and the failure is logged once with the job's name, because a sweep failing silently is
* indistinguishable from one that is not scheduled at all.
*
* Overlapping ticks join the one already running rather than being dropped, so awaiting `tick()`
* means the tick finished — which is what lets a test drive this deterministically. The timer is
* unref'd: background work is never the reason the process stays up.
*/
export declare function startDaemonTick(opts: DaemonTickOptions): DaemonTick;
//# sourceMappingURL=daemon-tick.d.ts.map