npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cool-ai/beach-starter

v1.5.0

Published

Canonical pipeline handlers and routing template — the working shape for a Beach application.

Downloads

757

Readme

@cool-ai/beach-starter

Canonical pipeline handlers and routing template for Beach applications. Owns the reference wiring that turns the router and its session primitives into a working application — channel-blind interior, reply delivery at the router layer.

Home: cool-ai.org · Documentation: cool-ai.org/docs

Three consumer projects rediscovered the same canonical-handlers pattern independently. This package extracts it so new consumers inherit the working shape rather than rediscovering it.

Install

npm install @cool-ai/beach-starter

Peer dependencies: @cool-ai/beach-core, @cool-ai/beach-llm.

A configured starting point — including a sample concierge handler, an example-lookup tool, and an sse streaming channel pre-wired for this pipeline — comes from @cool-ai/beach-config:

npm install @cool-ai/beach-config
npx beach-config init --with-starter

The flag lays down config/handlers/concierge.yaml, config/tools/example-lookup.yaml, and config/channels/sse.yaml alongside the regular config scaffold. Replace the placeholders with the application's real concierge and tools.

The canonical pipeline

channel inbound
  → channel:message_received      (channel-blind first-layer event;
                                   inbound adapter has resolved threadId,
                                   personId, destinations on the session)
  → [channel-router resolves the inbound to session:request:
     sessionId from threadId, mints the correlation eventId]
  → session:request               ({ sessionId, eventId, parts })
  → YOUR_HANDLER                  (router.register; deterministic or LLM-backed,
                                   runs its work, replies by routing
                                   session:reply — channel-blind)
  → [router delivers the reply to each session.destination]
  → delivery:ui_streaming         OR    delivery:formatter
  → YOUR delivery handler         (wired in your routing config; a thin route
                                   to your streaming transport, or a MissiveStore
                                   write for a batched Composer)

Multi-channel replies (a client emailed and texted; reply via both) work without channel-branching code anywhere — the inbound adapter populates two destinations on the session and the router delivers the reply to each after the handler settles.

Quick start — channel-blind application

import { EventRouter } from '@cool-ai/beach-core';
import { createLLMHandler } from '@cool-ai/beach-llm';
import { InMemoryMissiveStore } from '@cool-ai/beach-missives/stores';
import routingConfig from '@cool-ai/beach-starter/templates/routing.json' with { type: 'json' };

const router = new EventRouter();
const store = new InMemoryMissiveStore();

// 1. Build the orchestrator handler. createLLMHandler returns an EventHandler;
//    a deterministic handler is the same type.
const concierge = createLLMHandler({ config: handlerConfig, provider, registry: tools });

// 2. Register your orchestrator. Inbound resolution to session:request is the
//    channel-router's job — the starter ships no inbound handler.
router.register('concierge', concierge);

// Wire each delivery event to your own outbound path. The starter ships no
// reference delivery handlers — they are thin wrappers over existing components:
//   delivery:ui_streaming → your streaming transport (e.g. SSE/Redis publish)
//   delivery:formatter    → a handler that writes a draft to the MissiveStore
router.register('ui-stream', async (event) => {
  const { sessionId, parts } = event.data as { sessionId: string; parts: unknown };
  await redis.publish(`reply:${sessionId}`, JSON.stringify(parts));
});

// 3. Load the routing config (point the orchestrator placeholder at 'concierge' first).
router.loadRoutingConfig(routingConfig);

// 4. The inbound adapter opens each session with its destination set.
//    A chat session opens with a ui-streaming destination:
router.openSession({
  id: sessionId,
  destinations: [{ kind: 'ui-streaming' }, { kind: 'audit' }],
});

// An email session opens with a formatter destination:
//   destinations: [
//     { kind: 'formatter:email-html', formatterChannel: 'email-html' },
//     { kind: 'audit' },
//   ]
//
// A multi-channel session populates both — the router delivers the reply to
// each without any channel-branching code.

Handlers

The starter ships no reference handlers. Your orchestrator (deterministic or built with createLLMHandler) registers with router.register(name, handler) and consumes session:request.

Inbound resolution — channel:message_receivedsession:request — is the channel-router's job: it resolves the session from the inbound threadId, mints the correlation eventId, and emits the channel-blind session:request. Delivery is a thin route to existing components (a transport adapter; MissiveStore.write), so the starter ships no reference collectors for it either.

The canonical event tuples — including the delivery:* events you wire your own delivery handlers to — are exported as EVENTS.

Session resolution strategies

The inbound adapter populates threadId on the first-layer event before routing it; the channel-router resolves the session from it. Per-channel computation lives in the channel adapters:

| Channel adapter | Computes threadId from | |---|---| | @cool-ai/beach-channel-email | RFC 5322 In-Reply-To / References chain | | @cool-ai/beach-channel-whatsapp | quotedMessageId | | SSE / chat | session-cookie / session id from the HTTP layer |

Routing template

@cool-ai/beach-starter/templates/routing.json ships as the reference routing config. Import it with a JSON module assertion and pass it to router.loadRoutingConfig(). Replace the orchestrator placeholder with your registered handler id before loading.

Architectural discipline

This package is the concrete answer to design-principle 2.1 (the router is the boundary) and the channel-blind discipline. Consumers that bypass the canonical pipeline — calling an LLM directly from an HTTP route, for instance — lose every architectural guarantee Beach exists to provide: audit, observability, approval interception, replay, distributed tracing. A handler reads context.session (a SessionView with no destinations) and cannot name a channel; channel-aware logic belongs at the channel adaptor (@cool-ai/beach-channel-*), wired with a renderer (@cool-ai/beach-a2ui-renderer-*) and an optional composer (@cool-ai/beach-channel-composer).

The canonical pipeline is the working shape. Wire your handler into it — but do not skip it.

Related