Library / SDK
photon-hq/spectrum-ts avatar
photon-hq/spectrum-ts

spectrum-ts: Routing Agent Replies Through iMessage, SMS and Slack

Bring agents to any interfaces

1,702 stars218 forksTypeScriptMIT

At a glance

What is it?
Photon's MIT-licensed TypeScript framework puts a single async message loop in front of iMessage, WhatsApp Business, Telegram, Slack and a terminal provider. The hosted path is short; the self-hosted path is where the cost sits.
Who is it for?
Adopt spectrum-ts if your agent already lives in TypeScript and the channel you need is one of the listed providers, because the hosted route through Spectrum Cloud reduces the integration to a project ID, a project secret and a provider config.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem spectrum-ts is built around

Most agent frameworks assume the conversation happens in a browser tab you control. The README states the opposite premise for Photon: it builds infrastructure for AI agents that operate over real communication channels, and it describes Spectrum as a multi-channel agent framework that makes agents reachable over surfaces like iMessage, SMS, email, Slack, Discord and voice rather than confining them to web chat. The stated goal is narrower than "chatbot platform". It is the transport layer between an agent process and the messaging apps people already have open. That framing matters because it changes what the library has to own. It does not own the model, the prompt, or the conversation state machine. It owns the connection to each channel, the normalisation of inbound messages into one iterable shape, and the routing of replies back out through the same channel. The audience is a TypeScript developer who has an agent loop working locally and now needs it to answer an iMessage thread, a Telegram chat, or a Slack workspace without writing three separate webhook handlers and three separate send paths.

One async iterator over every provider

The mechanism visible in the README is an async iterable of message pairs. Spectrum is called with an options object containing projectId, projectSecret and a providers array, and the returned app exposes app.messages, which is consumed with for await. Each iteration yields a two-element tuple of space and message. The space object carries a responding method that takes an async callback, and inside that callback message.reply sends text back. Two details in that shape are worth naming. First, the responding wrapper is scoped: it delimits the window in which the agent is considered to be handling that conversation, which is where any typing indicator or read receipt behaviour would have to attach, though the README does not spell that out. Second, the channel identity is not in the loop body. The same code runs whether the provider is iMessage, Telegram or the terminal, because the provider was chosen at construction time in the providers array. Adding a channel means adding a config call to that array, not branching inside the handler. The README gives imessage.config() as the example, and the platform table implies the same pattern for each listed package.

Hosted credentials versus a local iMessage database

There are two deployment shapes and the README treats them as separate products. The hosted shape is Spectrum Cloud, described as hosted infrastructure for platforms like iMessage with credentials ready in minutes. You sign up at app.photon.codes, receive a project ID and secret, and the SDK connects outward. The standalone shape is the interesting one for anyone who cannot route messages through a third party. The README says Spectrum runs fully standalone, and lists three ways: connecting to a local iMessage database through the separate @spectrum-ts/imessage-local package, bringing your own gRPC endpoints, or building a platform provider with definePlatform. The README does not document the local database schema, the permissions iMessage requires, or what the gRPC contract looks like. It points at docs.photon.codes for self-hosted setups. That is a real gap in the repository itself: the standalone path is announced in one sentence and specified elsewhere.

Install commands and the provider package split

The README gives bun add spectrum-ts as the batteries-included install, covering the runtime plus the standard provider set, with local iMessage excluded as an explicit install. For a smaller footprint it gives bun add @spectrum-ts/core @spectrum-ts/telegram as the alternative, depending on the runtime plus only the providers you use, importing from the scoped packages directly. It also states that the spectrum-ts/providers/<platform> import paths work either way as long as the matching provider package is installed, and that if it is not, the import fails at build or startup naming the exact package to add. That failure behaviour is a deliberate design choice and a good one: a missing provider is a resolution error with a package name in it, not a runtime surprise after the process starts. The application code in the README reads projectId and projectSecret from process.env, which is the expected pattern for the hosted route. The provider list in the table covers iMessage, local iMessage, WhatsApp Business, Telegram, Slack and Terminal, with Custom pointing at definePlatform from spectrum-ts.

Where the framework stops helping

The README is honest about scope by omission. It shows reply as the outbound operation and says nothing about attachments, rich formatting, message editing, delivery receipts, retries, or rate limits per channel. Those are exactly the areas where channel providers diverge most: WhatsApp Business has template rules that iMessage does not, and Slack has threading and block formatting that SMS cannot carry. A framework that normalises inbound messages into one tuple shape has to either expose per-channel escape hatches or flatten the differences, and the README does not say which. The second limitation is the credential dependency. The hosted path requires a project ID and secret from Photon's own service, so the default experience is not self-contained even though the code is MIT-licensed. The standalone path removes that dependency but moves you onto documentation the repository does not contain. The third is version cadence: releases v12.7.0, v12.8.0 and v12.9.0 landed between late July and early September 2026, roughly three weeks apart, which means the major version is moving and you should pin exact versions rather than float.

How this differs from a bot framework like grammY

grammY is a reasonable comparison point because it is also TypeScript and also handles messaging, but the architecture is inverted. grammY targets one platform, Telegram, and goes deep on that platform's specifics: middleware chains, session storage, the Bot API's full surface. spectrum-ts targets many platforms and goes shallow on each, exposing a uniform space and message pair and delegating platform detail to provider packages. If Telegram is your only channel, grammY's middleware model will give you more of the platform than a normalised tuple will. If you need the same handler answering Telegram and iMessage, spectrum-ts solves a problem grammY does not attempt: grammY has no iMessage provider and no plan to abstract across channels. The trade is depth for breadth, and the choice follows directly from how many surfaces you actually need to cover. A single-channel product should not pay the abstraction cost.

Licence, maintenance and upgrade exposure

The licence is MIT, held by Photon, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is permissive and imposes no copyleft obligation on your application code. It does not cover the hosted service: Spectrum Cloud access is governed by whatever terms apply to app.photon.codes accounts, and the MIT grant on the SDK says nothing about that relationship. Treat the licence question and the service question as separate. On maintenance, the repository is not archived, the default branch is main, and the most recent push recorded is 2026-09-09, one day after the v12.9.0 release. The version numbering suggests a project that has already absorbed breaking changes across major iterations, and three minor releases in about six weeks means the provider packages and the core can drift apart. Pin spectrum-ts and every @spectrum-ts provider package to exact versions in your lockfile, and read the release notes before moving a minor version, because the README does not describe a compatibility matrix between core and providers.

Editorial conclusion

Adopt spectrum-ts if your agent already lives in TypeScript and the channel you need is one of the listed providers, because the hosted route through Spectrum Cloud reduces the integration to a project ID, a project secret and a provider config. Do not adopt it if your requirement is a channel outside that list and you are not prepared to write a provider with definePlatform, or if you need the local iMessage database path and have not confirmed that @spectrum-ts/imessage-local is still published and compatible with your macOS and iMessage versions. Verify three things before committing: that the provider package for your target channel resolves from npm at the version you pin, that your project ID and project secret are available from app.photon.codes, and that the standalone gRPC endpoints you intend to point at are documented at docs.photon.codes.

Official sources

  1. License: MIT
  2. photon-hq/spectrum-ts on GitHub
  3. Project website
  4. README
  5. Releases
Community notes

Community notes