How the ADHD onEvent Callback Streams Progress and Events
The ADHD onEvent callback streams real-time progress through eight distinct event types emitted during the four-phase execution pipeline in src/engine.ts, enabling non-blocking monitoring of reframing, framing, scoring, clustering, and deepening operations.
The onEvent callback is a core mechanism in the ADHD repository (UditAkhourii/adhd) for observing the internal state of the creative engine without blocking the main execution flow. By passing an optional callback to the run function, developers can receive structured progress notifications as the system transforms an input problem into clustered, deepened ideas.
The RunEvent Type and Event Schema
The complete event contract is declared in src/types.ts, where the RunEvent type defines every possible notification the engine can emit. Each event is a plain object with a kind discriminator and a payload specific to that phase.
The engine emits the following event kinds during a standard run:
reframe:done– Emitted after optional anchor-stripping, carrying{ changed: boolean }to indicate whether the problem text was modified.frame:start– Fired before diverging a new frame, including{ frameId: string; frameLabel: string }.frame:done– Sent after a frame’s ideas are generated, reporting{ frameId: string; count: number }.score:done– Indicates all ideas have been scored, passing{ total: number }.cluster:done– Signals completion of clustering with{ clusters: number }.deepen:start– Marks the beginning of a deepening pass for a top-K idea, carrying{ ideaId: string; text: string }.deepen:done– Confirms completion of deepening for a specific idea with{ ideaId: string }.warn– Used by internal helpers to surface non-fatal issues such as API throttling, containing{ message: string }.
Four-Phase Progress Streaming
The run function in src/engine.ts orchestrates execution through four distinct phases, invoking onEvent at major transition points to create a lightweight streaming protocol.
Phase 0: Reframe
If stripAnchors is enabled in RunOptions, the engine first calls reframeProblem to clean the input. Immediately after this optional step, the callback receives:
onEvent?.({ kind: "reframe:done", changed: true });
This occurs at lines 46–47 of src/engine.ts, allowing callers to log whether the original problem was modified before processing continues.
Phase 1: Diverge (Frames)
During the divergence phase, the engine iterates over selected frames and generates raw ideas for each. For every frame, the callback is invoked twice:
- Before generation –
frame:startis emitted at line 56, right beforedivergeBranchis called. - After generation –
frame:doneis emitted at line 58, reporting the quantity of ideas produced by that frame.
This creates a nested progress indicator showing which cognitive frame is active and how productive it was.
Phase 2: Score and Cluster
Once all frames have diverged, the engine batches the ideas through scoring and clustering. At line 77, after scoreIdeas completes, the engine emits:
onEvent?.({ kind: "score:done", total: ideas.length });
Immediately following the clustering operation at line 78, it emits:
onEvent?.({ kind: "cluster:done", clusters: clusters.length });
These events provide quantitative milestones for tracking dataset reduction as the engine moves from broad ideation to focused synthesis.
Phase 3: Deepen (Focus)
The final phase deepens the top-K ranked ideas. For each selected idea, the engine wraps the deepenIdea call with start and done events at lines 102 and 104:
onEvent?.({ kind: "deepen:start", ideaId: idea.id, text: idea.text });
// ... deepening logic ...
onEvent?.({ kind: "deepen:done", ideaId: idea.id });
This pairing allows interfaces to display "focus" indicators or spinners while individual ideas are being expanded, creating a responsive user experience during the longest-running sub-operation.
Implementing the onEvent Callback
To consume the progress stream programmatically, provide a function matching the onEvent signature when calling run:
import { run, RunEvent } from "adhd";
const result = await run({
problem: "How to improve remote collaboration?",
stripAnchors: true,
onEvent: (e: RunEvent) => {
switch (e.kind) {
case "frame:start":
console.log(`Exploring frame: ${e.frameLabel}`);
break;
case "deepen:done":
console.log(`Completed deep-dive on idea ${e.ideaId}`);
break;
case "warn":
console.error(`Warning: ${e.message}`);
break;
}
},
});
If onEvent is omitted or set to undefined, the engine runs silently, making the callback ideal for both verbose CLI tools and silent server-side batch processing.
CLI Integration Example
The command-line interface in src/cli.ts demonstrates production usage of the callback for human-readable progress output. When the --quiet flag is absent, the CLI wires onEvent to stderr with concise status indicators:
const onEvent = flags.quiet ? undefined : (e: RunEvent) => {
switch (e.kind) {
case "reframe:done":
if (e.changed) process.stderr.write(` ↺ anchors stripped from problem\n`);
break;
case "frame:start":
process.stderr.write(` ▸ ${e.frameLabel}…\n`);
break;
case "frame:done":
process.stderr.write(` ${e.count} ideas (${e.frameId})\n`);
break;
case "score:done":
process.stderr.write(` scored ${e.total} ideas\n`);
break;
case "cluster:done":
process.stderr.write(` ${e.clusters} clusters\n`);
break;
case "deepen:start":
process.stderr.write(` ◎ focus → ${e.text}\n`);
break;
case "warn":
process.stderr.write(` ! ${e.message}\n`);
break;
}
};
This implementation (lines 20–30 of src/cli.ts) maps each RunEvent to a single terminal line, providing real-time visibility into the engine's four-phase pipeline without cluttering stdout, which remains reserved for the final JSON result.
Summary
- The ADHD
onEventcallback is defined insrc/types.tsas(e: RunEvent) => voidand passed viaRunOptionsto therunfunction insrc/engine.ts. - The engine emits eight distinct event types across four execution phases: reframing, framing (divergence), scoring/clustering, and deepening.
- Phase-specific payloads provide contextual data such as frame labels, idea counts, cluster totals, and warning messages.
- The callback is fully optional; when undefined, the engine suppresses all progress notifications for silent operation.
- The CLI implementation in
src/cli.tsserves as a reference for mappingRunEventobjects to user-facing progress indicators.
Frequently Asked Questions
What is the TypeScript signature of the onEvent callback in ADHD?
The onEvent callback is typed as (e: RunEvent) => void and is passed as an optional property within the RunOptions object to the run function exported from src/engine.ts. The RunEvent type is a discriminated union defined in src/types.ts that includes variants for reframing, framing, scoring, clustering, deepening, and warnings.
Which events are emitted during the framing phase?
During the framing phase, the engine emits frame:start immediately before calling divergeBranch for a specific frame, and frame:done immediately after the frame's ideas have been generated. These events include the frameId and frameLabel (on start) and the count of ideas produced (on done), allowing precise tracking of which cognitive frames are active and their productivity.
How does the CLI handle quiet mode with onEvent?
The CLI in src/cli.ts conditionally sets onEvent to undefined when the --quiet flag is present. When quiet mode is disabled, it provides a handler that writes formatted progress messages to stderr, ensuring that stdout remains clean for the final JSON output while still providing human-readable status updates during execution.
Can I use onEvent for logging or analytics?
Yes, the onEvent callback is designed for observation without side effects on the core engine logic. You can use it to write structured logs to a file, send telemetry to an analytics service, or update a progress bar in a GUI application. Because the callback is invoked synchronously at phase boundaries, it should remain lightweight to avoid blocking the generation pipeline.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →