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

@personize/signal

v0.1.0

Published

Smart notification engine powered by Personize SDK — context-aware customer engagement with AI decision-making, workspace collaboration, and governed delivery.

Downloads

45

Readme

@personize/signal

Smart notification engine built on the Personize SDK. Decides IF, WHAT, WHEN, and HOW to notify each person using unified memory and governance guardrails.

Delivery channels and event sources are modular — the community adds these. The core intelligence is Personize.

Quick Start

npm install @personize/signal @personize/sdk
import { Personize } from '@personize/sdk';
import { Signal, ConsoleChannel, ManualSource } from '@personize/signal';

const client = new Personize({ secretKey: process.env.PERSONIZE_KEY! });
const manual = new ManualSource();

const signal = new Signal({
    client,
    channels: [new ConsoleChannel()],
    sources: [manual],
});

await signal.start();

// Push an event — Signal decides whether to notify
manual.emit({
    id: 'evt_001',
    type: 'user.signup',
    email: '[email protected]',
    data: { plan: 'pro', source: 'website' },
    timestamp: new Date().toISOString(),
});

Or trigger synchronously and get the AI decision:

const result = await signal.trigger({
    id: 'evt_002',
    type: 'usage.drop',
    email: '[email protected]',
    data: { metric: 'daily_logins', dropPercent: 40 },
    timestamp: new Date().toISOString(),
});

console.log(result.action);    // 'SEND' | 'DEFER' | 'SKIP'
console.log(result.score);     // 0-100
console.log(result.reasoning); // AI explanation

How It Works

Event In           Pre-Check          Context Assembly         AI Decision
  |                   |                     |                      |
Source ──> [ dedup + daily cap ] ──> [ 4 parallel SDK calls ] ──> [ prompt() ]
  |        (no SDK calls)           |  smartContext (rules)    |    |
  |        Fast skip if:            |  smartDigest (entity)    |   SEND ──> channel.send()
  |        - Same event < 6h ago    |  smartRecall (context)   |   DEFER ──> memorize + digest
  |        - Over daily cap (5/day) |  smartRecall (sent log)  |   SKIP ──> log, done
  |                                                                |
  └────────────────────────── EngineResult ───────────────────────-┘

Engine Flow (9 Steps)

  1. ReceiveSignalEvent enters via Source or signal.trigger()
  2. Pre-check — dedup + daily cap (no SDK calls, instant)
  3. Context assembly — 4 parallel SDK calls: governance rules, entity digest, semantic context, recent notifications sent
  4. AI decisionprompt() scores 0-100: SEND (>60), DEFER (40-60), SKIP (<40)
  5. Decision routing — branch on SEND/DEFER/SKIP
  6. Deliverchannel.send() (if SEND)
  7. Workspace update — optional task/update creation on the entity record
  8. Feedback — memorize what was sent back to the entity record
  9. ReturnEngineResult with action, score, reasoning, delivery status, duration

Built-in Channels

| Channel | Use Case | Config | |---------|----------|--------| | ConsoleChannel | Development & testing | None | | SesChannel | AWS SES email | { sourceEmail, region? } | | SendGridChannel | SendGrid email | { apiKey, fromEmail, fromName? } | | SlackChannel | Slack webhooks | { webhookUrl } | | InAppChannel | Callback-based (bridge to your UI) | handler: (recipient, payload) => ... |

Built-in Sources

| Source | Use Case | Config | |--------|----------|--------| | ManualSource | Programmatic events from your code | None — call .emit(event) | | WebhookSource | HTTP webhooks into Signal | { path?, secret?, parser? } |

Workspace Utilities

Signal includes convenience methods for the Personize workspace pattern — collaborative workstations per entity record:

// Add tasks, notes, updates, issues to any entity record
await signal.workspace.addTask('[email protected]', {
    title: 'Follow up on trial expiry',
    priority: 'high',
    assignee: 'sales',
});

await signal.workspace.addNote('[email protected]', {
    content: 'Showed strong interest in enterprise features during demo',
    tags: ['sales', 'enterprise'],
});

await signal.workspace.addUpdate('[email protected]',
    'Onboarding completed — activated 3 integrations');

// Read back workspace data
const tasks = await signal.workspace.getTasks('[email protected]');
const digest = await signal.workspace.getDigest('[email protected]');

When engine.workspaceUpdates is enabled, the engine automatically:

  • On SEND — creates an update: "Notification sent: <subject>"
  • On DEFER — creates a task: "Review deferred notification for <entity>"

Digest Pipeline

Deferred notifications (score 40-60) are stored in Personize memory with signal:deferred tags. The digest builder compiles them into periodic personalized summaries:

// Run digest for one user
const result = await signal.digest.buildForUser('[email protected]');

// Run batch digest for all users (use with scheduler)
const batch = await signal.digest.runBatch([
    '[email protected]',
    '[email protected]',
]);

// Schedule automatic daily digests
signal.schedule('daily-digest', '0 9 * * 1-5', async () => {
    const users = await getActiveUsers(); // your function
    await signal.digest.runBatch(users);
});

Multi-Team Patterns

Multiple teams contribute to the same entity records. Each team tags their events:

// Product team
manual.emit({
    type: 'usage.milestone',
    email: '[email protected]',
    data: { milestone: '100_api_calls' },
    metadata: { team: 'product' },
    // ...
});

// Sales team
manual.emit({
    type: 'deal.stage_changed',
    email: '[email protected]',
    data: { stage: 'proposal', rep: 'mike' },
    metadata: { team: 'sales' },
    // ...
});

When Signal evaluates any event, smartDigest returns the combined context from ALL teams. The AI decides based on the full cross-team picture — product usage + sales activity + marketing engagement.

Cost Controls

Signal is designed to be cost-efficient with the Personize API:

const signal = new Signal({
    client,
    channels: [...],
    engine: {
        dailyCap: 5,                        // max notifications per email per day
        deduplicationWindowMs: 6 * 60 * 60 * 1000, // skip same event type within 6h
        maxEvaluationsPerMinute: 20,         // rate limit for batch processing
        concurrency: 5,                      // max parallel evaluations
        memorize: true,                      // feedback loop (default)
        workspaceUpdates: false,             // workspace tasks/updates (default: off)
    },
});

Pre-check skips events before any SDK calls:

  • Same event type for same email within dedup window → instant SKIP
  • Email over daily cap → instant SKIP

Per evaluation: typically 4-5 SDK calls (smartContext + smartDigest + 2x smartRecall + prompt). Pre-check reduces this to 0 for obvious skips.

Adding a Channel

See templates/CHANNEL_TEMPLATE.md. One interface, one file, one test.

import type { Channel, Recipient, DeliveryPayload, DeliveryResult } from '@personize/signal';

export class TwilioChannel implements Channel {
    name = 'twilio';

    async send(recipient: Recipient, payload: DeliveryPayload): Promise<DeliveryResult> {
        // Your delivery logic
        return { success: true, channel: this.name, timestamp: new Date().toISOString() };
    }
}

Adding a Source

See templates/SOURCE_TEMPLATE.md. One interface, one file, one test.

import { EventEmitter } from 'events';
import type { Source, SignalEvent } from '@personize/signal';

export class StripeSource implements Source {
    name = 'stripe';

    async start(emitter: EventEmitter): Promise<void> {
        // Listen for Stripe webhooks, emit SignalEvents
    }

    async stop(): Promise<void> {
        // Cleanup
    }
}

Configuration Reference

SignalConfig

| Field | Type | Required | Default | Description | |-------|------|----------|---------|-------------| | client | Personize | Yes | — | Authenticated SDK client | | channels | Channel[] | Yes | — | At least one delivery channel | | sources | Source[] | No | [] | Event sources | | scheduler | Scheduler | No | CronScheduler | Scheduler implementation | | engine | EngineConfig | No | See below | Engine configuration |

EngineConfig

| Field | Type | Default | Description | |-------|------|---------|-------------| | memorize | boolean | true | Record sent notifications in memory | | concurrency | number | 5 | Max parallel evaluations | | dailyCap | number | 5 | Max notifications per email per day | | deduplicationWindowMs | number | 21600000 (6h) | Dedup window in ms | | maxEvaluationsPerMinute | number | 20 | Rate limit for batch operations | | workspaceUpdates | boolean | false | Create workspace entries on SEND/DEFER |

Examples

See the examples/ directory:

Architecture

┌──────────────────────────────────────────────────────────┐
│  Your Application                                        │
│  ┌─────────┐  ┌───────────┐  ┌────────────┐             │
│  │ Sources  │  │  Signal   │  │  Channels  │             │
│  │ ──────── │  │  Engine   │  │  ────────  │             │
│  │ Manual   │─>│ (9 steps) │─>│ SES        │             │
│  │ Webhook  │  │           │  │ SendGrid   │             │
│  │ HubSpot* │  │ Pre-check │  │ Slack      │             │
│  │ Stripe*  │  │ Context   │  │ InApp      │             │
│  └─────────┘  │ Decision  │  │ Twilio*    │             │
│               │ Deliver   │  └────────────┘             │
│               │ Feedback  │                              │
│               └─────┬─────┘                              │
│                     │                                    │
│               ┌─────┴─────┐                              │
│               │ Personize │  * = community-contributed   │
│               │    SDK    │                              │
│               │ ───────── │                              │
│               │ Memory    │                              │
│               │ Governance│                              │
│               │ AI        │                              │
│               └───────────┘                              │
└──────────────────────────────────────────────────────────┘

Core (Personize SDK — not pluggable): Memory, Intelligence, Governance, Workspaces

Edges (modular — community contributes): Channels, Sources, Schedulers

License

Apache-2.0 — see LICENSE.