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

@baremetallabs-ai/agent-apps-notifications

v0.1.2

Published

`@baremetallabs-ai/agent-apps-notifications` is the public, dependency-free Node 22+ contract for first-party AgentMail, AgentChat, and AgentPhone webhook notifications. It can be used by the AgentApps relay and eve notification channel without `@sovereig

Downloads

539

Readme

AgentApps notifications

@baremetallabs-ai/agent-apps-notifications is the public, dependency-free Node 22+ contract for first-party AgentMail, AgentChat, and AgentPhone webhook notifications. It can be used by the AgentApps relay and eve notification channel without @sovereign-ai/core. Publication is tracked in #1758; relay and eve integration are #1759 and #1760.

Install and API

npm install @baremetallabs-ai/agent-apps-notifications

The root export provides parseNotification(payload, sender, idempotencyKey): NotificationDelivery, signNotificationBody(rawBody, currentSecret): string, verifyNotificationBody(rawBody, signatureHeader, currentSecret, previousSecret?): boolean, formatFileOfferOutcome(notice), NotificationValidationError, and the notification and receipt types. sender is a trusted AuthenticatedNotificationSender, derived from authenticated sender or install credentials. JSON fields never establish app identity or adoption permission.

A receiver reads the original request bytes (HQ caps webhook bodies at 1 MiB), authenticates the sender or install, verifies X-Webhook-Signature, then decodes JSON and parses the notification. Never verify a reserialized object. The sender signs only with the current secret. A receiver can also accept one previous secret during rotation; this contract does not set rotation timing.

import {
  parseNotification,
  signNotificationBody,
  verifyNotificationBody,
} from '@baremetallabs-ai/agent-apps-notifications';

const rawBody = new TextEncoder().encode(
  JSON.stringify({
    correlationId: 'agentmail:thread-1',
    content: 'New mail received.',
  })
);
const signature = signNotificationBody(rawBody, 'current-secret');
const sender = { app: 'agentmail' } as const; // Obtained from authenticated credentials.
if (!verifyNotificationBody(rawBody, signature, 'current-secret', 'previous-secret')) {
  throw new Error('Invalid webhook signature');
}
const delivery = parseNotification(
  JSON.parse(new TextDecoder().decode(rawBody)),
  sender,
  'mail-delivery-1'
);
// Atomically claim and persist the delivered message and its receipt in the consumer store.

Every request requires a nonblank Idempotency-Key. X-Webhook-Signature is sha256= plus 64 hexadecimal characters. HMAC-SHA-256 covers the exact original request-body bytes. Modified bytes or an invalid signature fail authentication. The package's parser receives decoded JSON only after verification.

The parser trims the header delivery key. For ordinary messages it rejects whitespace-only correlationId or content and returns both trimmed, matching HQ's stored content and receipt comparison. Signature verification still uses the original body bytes before this normalization. File-offer body fields remain untrimmed; a blank teams: suffix or whitespace-only offerId is invalid.

Wire forms and permissions

Ordinary message JSON has required nonempty correlationId and content strings, plus only the app-specific optional claim below. Unknown fields, schema, attachments, kind, and fields of the other form are rejected. The app comes from credentials, not JSON.

| Authenticated app | Correlation | Optional claim | Delivery key | | ----------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | AgentMail | agentmail:<nonempty threadId> | sourceMessageId, a canonical agentmail-message:v1: base64url JSON object with exactly nonempty mailboxId, messageId, threadId; the thread must match the correlation | Any nonblank key | | AgentChat | teams:<nonempty conversation> | inboundFileContext only with an activity key | Any nonblank key; context requires agentchat-teams-activity: followed by 43 base64url characters | | AgentPhone | phone:<lowercase UUIDv4 callId> | originatingSessionId only when allowOriginatingSessionAdoption is true | Call event: agentphone-call-event:<callId>:connected or :completed; consult: agentphone-consult:<64 lowercase hex>; other nonblank keys are allowed |

AgentPhone consults have the same JSON shape as ordinary phone messages. They require adoption permission and a valid originatingSessionId. The consumer additionally checks its stored consult record's call, origin, pending or delivered state, and exact original request body before completing or replaying delivery. The package cannot establish that a referenced session exists; the consumer decides whether to adopt it.

The origin reference must be nonblank, at most 1,500 UTF-8 bytes, with valid UTF-16, and cannot be . or .., contain /, or use the reserved __...__ form. This deliberately rejects disabled-adoption, malformed-origin, and non-phone origin claims that HQ currently accepts and ignores or classifies as unusable. HQ remains unchanged.

inboundFileContext contains exactly hasUsableText (boolean), failures (array), and stagedFileIndexes (empty array for these webhooks). Each failure contains index (nonnegative integer), optional nonempty name, reason, optional reason-dependent recoveryDisposition, and optional operatorEvidence with exactly attemptCount, elapsedMs, recordedAt, and expiresAt. Counts are nonnegative integers; timestamps must parse with Date.parse. Failure indexes increase strictly and form a dense zero-based partition with staged indexes. Legacy reasons (unusable_metadata, count_limit, size_limit, source_fetch_failed, staging_failed, workspace_delivery_failed, unsupported_item, not_supported_in_conversation) disallow disposition. Dynamic reasons (transient_timeout, credential_rejected, service_failure, transfer_failure, staging_incomplete, source_unavailable, ingress_unavailable, recovery_budget_exhausted, staging_refused, unsupported_runtime) require one of exhausted, unavailable_on_immediate_path, not_retryable, not_attempted.

AgentChat file-offer outcomes are a distinct eight-field body: kind: "file_offer_outcome", correlationId, offerId, outcome, createdAt, expiresAt, settledAt, idempotencyKey. Only an authenticated AgentChat sender can use this form. The body key and header must both equal agentchat-file-offer:<offerId>. The terminal outcomes are delivered, declined, expired, unavailable_or_changed, authorization_refused, delivery_failed, and delivery_unknown. Timestamps must parse with Date.parse; chronological ordering is not required. formatFileOfferOutcome provides HQ-compatible delivered text.

Representative requests

All examples send Content-Type: application/json, the displayed Idempotency-Key, and X-Webhook-Signature: sha256=<HMAC-SHA-256 of the exact JSON bytes with the current secret>. Construct the header with signNotificationBody(rawBody, currentSecret). Each successful first delivery returns HTTP 201 with {"sessionId":"session-1","messageId":"message-1"}; an unchanged completed retry returns the identical status and body.

{
  "correlationId": "agentmail:thread-1",
  "content": "New mail received.",
  "sourceMessageId": "agentmail-message:v1:eyJtYWlsYm94SWQiOiJtYWlsYm94LTEiLCJtZXNzYWdlSWQiOiJtZXNzYWdlLTEiLCJ0aHJlYWRJZCI6InRocmVhZC0xIn0"
}

AgentMail inbound uses a delivery key such as mail-inbound-1. Outbound resolution omits sourceMessageId.

{
  "correlationId": "teams:conversation-1",
  "content": "An attachment could not be staged.",
  "inboundFileContext": {
    "hasUsableText": false,
    "failures": [
      {
        "index": 0,
        "reason": "transient_timeout",
        "recoveryDisposition": "exhausted",
        "operatorEvidence": {
          "attemptCount": 2,
          "elapsedMs": 1000,
          "recordedAt": "2026-01-01T00:00:00Z",
          "expiresAt": "2026-01-02T00:00:00Z"
        }
      }
    ],
    "stagedFileIndexes": []
  }
}

The AgentChat context example uses Idempotency-Key: agentchat-teams-activity:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA. Personal and shared chat messages without context use the same ordinary form.

{
  "kind": "file_offer_outcome",
  "correlationId": "teams:conversation-1",
  "offerId": "offer-1",
  "outcome": "delivered",
  "createdAt": "2026-01-01T00:00:00Z",
  "expiresAt": "2026-01-02T00:00:00Z",
  "settledAt": "2026-01-01T01:00:00Z",
  "idempotencyKey": "agentchat-file-offer:offer-1"
}

The file-offer header key is also agentchat-file-offer:offer-1.

{
  "correlationId": "phone:123e4567-e89b-42d3-a456-426614174000",
  "originatingSessionId": "session-1",
  "content": "Call connected."
}

The phone call example uses Idempotency-Key: agentphone-call-event:123e4567-e89b-42d3-a456-426614174000:connected. Completion uses :completed. A consult delivery uses the same body shape and a key such as agentphone-consult:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.

Receipt rules and delivery guarantee

A consumer atomically claims (authenticated sender/install, conversation, delivery key), creates or enqueues one message, and stores a completed HTTP 201 receipt with sessionId and messageId. An in-progress same-content retry does not create a duplicate. A completed same-content retry returns the stored receipt unchanged. Reusing the key with changed delivered content conflicts. Authenticate and validate claims on every attempt, including replay.

For ordinary messages compare correlationId, content, and optional inboundFileContext. Validated sourceMessageId and originatingSessionId are identity claims and do not change ordinary delivered-content comparison. For file offers compare correlationId and the formatted delivered content. Consults additionally require the matching stored consult record and exact original body. The package exports types and rules; atomic persistence and retention belong to each consumer.

Delivery is at least once when the sender retries and best effort otherwise. This contract does not promise exactly-once delivery. Mandatory Idempotency-Key and optional previous-secret verification are new contract rules relative to today's HQ receiver.

Executable conformance examples: src/notification.test.ts, src/auth.test.ts, src/receipt.test.ts. Run npm run test in this workspace. npm run verify:package packs the package and compiles/runs a clean consumer outside the workspace. This package changes no first-party sender, HQ, AgentApps relay, eve receiver, rotation timing, or npm publication behavior.

Inbound AgentPhone texts use phone-text:<64 lowercase hex> as the correlation and agentphone-text:<64 lowercase hex> as the required Idempotency-Key. During transition, the legacy JSON body contains only correlationId and content. New senders include sourceMessageId equal to the idempotency key, senderNumber as canonical E.164, and conversationBinding: { purchaseOperationId, destinationNumber }. The signed binding lets the receiver verify that the sender number belongs to the correlation. The parser returns the validated source ID and sender number as facts and keeps the binding internal. Group texts add remoteNumbers with the complete ordered remote member set. The parser checks the group correlation against that set and the binding. A revised conversation also supplies providerConversationSid and providerRevision so the revision correlation can be verified. Group claims remain behind the worker's identity-claims rollout flag. Partial or inconsistent claims fail before delivery. Text delivery cannot adopt an originating session or carry file context. Deploy AgentApps, Eve, and HQ receivers that accept both forms before enabling the worker's complete-form sender. Set AGENTPHONE_TEXT_IDENTITY_CLAIMS=true on the AgentPhone events worker only after the accepting AgentApps, Eve, and HQ receivers are deployed. The worker defaults to the legacy body; its idempotency key and rendered content remain the same in either mode.

Maintainer release

Source: https://github.com/baremetallabs-ai/sovereign-ai. This package and the eve extension version independently under SemVer. Commit each new manifest version and lockfile entry before requesting a release; publication never renames or bumps a package. The first version was 0.1.0; the current contract is 0.1.2.

The @baremetallabs-ai npm organization uses the existing short-lived NPM_TOKEN for manual releases. CI runs the notifications 0.1.2 dry run first, then the Eve 0.1.4 dry run. After merge, the orchestrator manually dispatches Publish AgentApps npm package with package=notifications, version=0.1.2, dry_run=false, then package=eve, version=0.1.4, dry_run=false. Normal merges and tags never publish. A dry run uses dry_run=true and cannot write to the registry.

A private source repository reports provenance disabled: private source repository and must pass every other gate. New releases from a public source require provenance. Historical private-source releases do not need retroactive attestation. Every exact registry version, including an already occupied one, must pass packed-content, whole-tarball secret, clean-consumer, and registry-signature verification. An unsafe occupied version stops the release without choosing another version.

A published contract remains available if the extension fails. The pair is pair incomplete until the exact Eve version passes verification; retry the same versions to verify an occupied package as a no-op or publish the missing one. Trusted publishing can replace the token later; the workflow retains id-token: write for that switch.