npm / spectrum-ts
spectrum-ts
What changed in spectrum-ts 12, from its official release notes, upgrade guides and advisories. Sources checked 2026-11-10.
4 breaking2 security19 new APIs and options4 behavior and defaults16 fixes and other
See which of these apply to your code, ranked, in your agent:
npx @unstale/cli initspectrum-ts 1245 changes
Breaking changes 4
Outbound iMessage and WhatsApp Business polls without titles are rejected before sending because those providers require a title.
Spectrum no longer registers SIGINT/SIGTERM handlers or calls process.exit() automatically, allowing host applications to finish graceful shutdown. Standalone scripts that relied on automatic handling should call await app.stop() in their own signal handler.
Webhook signature verification rejects empty secrets with a clear configuration error.
The function is now async, so callers need to await it; verification uses Web Crypto instead of Node-only crypto APIs.
Security 2
An OIDC preflight check verifies npm trusted publishing for every publishable package before any version bump, tag, or GitHub release is created.
Unsupported native iMessage rows use a small marker instead of leaking raw Advanced iMessage data through custom content, while preserving useful message metadata.
New APIs and options 19
iMessage exposes optional verified sender device identity metadata on inbound messages,
space.getMessage(), and multipart message parts.WhatsApp Business supports typing indicators through this API.
WhatsApp Business supports starting typing indicators anchored to the inbound message for streams and webhooks.
Supports provider-supplied diagnostic metadata, giving provider authors better visibility into stuck stream work without changing delivery behavior.
iMessage exposes subscription-scoped heartbeat observations for live messages, polls, and groups streams without changing existing message payloads.
Provider authors receive signal in lifecycle.createClient to use for startup I/O and cleaning up partial resources.
Spectrum() accepts a startup-only signal so apps can cancel slow initialization, including project lookup, cloud token requests, and Telegram webhook registration.
Spectrum throws SpectrumStartupCleanupError when cleanup fails, with the original startup failure as the cause.
Adds built-in chat background presets via preset names such as skyDusk; names are type-checked and invalid names fail at runtime before anything is sent.
iMessage delivers native poll creation and option-add events as poll messages, including pollMessageGuid, change, and per-option optionIdentifier values for reliable tracking.
A manual release option allows intentional token-based recovery publishes to skip the OIDC preflight check.
asApp() can accept a known layout directly, avoiding a refetch of link metadata when a provider already has the card text.
iMessage native metadata now includes mini app details such as app name, bundle/team IDs, session ID, live state, URL, and decoded card layout.
A portable entrypoint for verifying and parsing native Spectrum webhook deliveries is available across runtimes including Cloudflare Workers, Deno Deploy, Convex, Node, and Bun.
New metadata types are exported so TypeScript users can safely consume the fields after narrowing with imessage.is().
Inbound messages, space.getMessage(), and native outbound send results expose curated native metadata, including delivery state, formatting, mentions, attachments, reactions, stickers, and system fields.
Provider authors can merge streams whose source list can change while the app is running.
iMessage delivers inbound read receipts as read messages, with the reader in message.sender and the original outbound message in message.content.target.
Exports readSchema from @spectrum-ts/core/authoring for callers that build or validate read content directly.
Behavior and default changes 4
For app-created spaces, pass the message to
typing("start", message)as the typing indicator anchor.Remote iMessage streams log diagnostics when event mapping or downstream delivery stays pending for 30 seconds, separating mapping from emit and excluding sensitive content.
iMessage inbound mini app cards now come through as Spectrum app content, so they can be handled like other app-style messages instead of falling back to unsupported content.
Read receipt targets are fully built Message objects, exposing the original content and outbound affordances such as edit() and unsend().
Fixes and other changes 16
A cached sender device identity is retained when a later server fetch omits it, preventing metadata refreshes from erasing verified identity data.
Typing indicators quietly skip partial targets or messages Meta cannot use as typing anchors instead of throwing.
Refreshes delivery/read state from the server even when a sent message is cached, while preserving cached content like mini app card sessions.
Return undefined when the refreshed parent does not contain a child, avoiding stale cached fallbacks.
Cleanup for iMessage, WhatsApp Business, Slack, and Fusor cancels active token refreshes, awaits retired lines, and surfaces cleanup errors clearly.
When startup fails after providers create clients, Spectrum rolls those clients back before rejecting instead of leaving partial resources running.
Vote events are correlated with native option identifiers instead of option titles, avoiding missed or mismatched vote events for native polls.
Webhook deserialization preserves incoming poll and poll_option content, giving webhook users the same poll shape as stream users.
The --provenance and --dry-run options are now passed through to npm publish correctly.
Release failures now show real OIDC diagnostics instead of vague E404 errors.
Streamed iMessage sends return the final nativeText after edits instead of retaining stale first-send metadata.
iMessage and WhatsApp Business cloud clients pick up newly provisioned or removed lines on token renewal.
An empty cloud token payload represents no lines provisioned, rather than leaving stale lines active.
Closing message streams cleans up line observers so old streams are not called during later line updates.
If the Cloud profile check fails, contact sharing stays disabled and retries later without interrupting inbound message streaming.
Contact sharing now re-checks the project profile sync setting, so turning profile sync on or off in Cloud no longer requires a client restart.