v0.1.0
First release.
lull is the part every chat agent rewrites badly. It waits for a conversation to fall quiet, then hands you one turn instead of four.
What it handles:
- Fragmented messages. Balloons are coalesced into a single turn after
quietMsof silence, capped bymaxWaitMs. Across 1,000 simulated conversations that is 71.3% fewer model calls (npm run bench, reproducible). - Redelivered webhooks. The same
messageIdtwice produces one event, tracked in a bounded window. - Human takeover. The bot goes quiet for a TTL, and messages arriving meanwhile are dropped rather than queued.
- Session expiry. The next turn after inactivity carries
isNewSession: true.
The core is a pure reducer that never reads the clock, creates a timer, or touches the network. It returns effects as data, and the facade executes them. That makes concurrency behaviour testable as arithmetic: 30 tests at this tag, three of them property-based, none of them sleeping.
See the CHANGELOG for the full list and the known limitations.