Skip to content

fix(photon): migrate user API to Spectrum backend + send-path & status follow-ups - #42602

Closed
underthestars-zhy wants to merge 6 commits into
NousResearch:mainfrom
photon-hq:ryan/photon-fix
Closed

fix(photon): migrate user API to Spectrum backend + send-path & status follow-ups#42602
underthestars-zhy wants to merge 6 commits into
NousResearch:mainfrom
photon-hq:ryan/photon-fix

Conversation

@underthestars-zhy

Copy link
Copy Markdown
Contributor

What does this PR do?

Five focused follow-ups on top of the now-merged gRPC-native Photon channel (PR #42444, landed as 4e4d27875…3b983e779). These harden the user/management plane and the outbound send path that shipped with that rework.

The headline change is moving the Spectrum user API off the Dashboard API (Bearer token) and onto the Spectrum API (Basic auth with project credentials) — the Dashboard endpoints were the wrong backend for user management and don't return the data the channel needs. The rest are smaller correctness fixes: markdown stripping + send retries, group flattening in the SDK config, persisting/displaying phone numbers in status, and bumping the SDK to the latest release.

This branch is rebased on current main, so it contains only these 5 commits.

Related Issue

Follow-up to #42444 (merged). No separate tracking issue.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

Migrate user API calls to the Spectrum backend

  • list_users, find_user_by_phone, create_user, register_user_if_absent, and refresh_user_numbers now hit the Spectrum API ({spectrum_host}/projects/{id}/users/) with Basic auth built from the project credentials, instead of the Dashboard API with a Bearer token. — auth.py, cli.py
  • Added a _spectrum_host() resolver, a _basic(project_id, project_secret) header helper, and structured error helpers (_response_error_detail / _raise_for_status); response unwrapping now handles Spectrum's nested data.users envelope. create_user sends {"type": "shared", ...}. — auth.py
  • New PHOTON_SPECTRUM_HOST env var (default https://spectrum.photon.codes). — plugin.yaml, README.md, website/docs/user-guide/messaging/photon.md

Outbound send: strip markdown + retry

  • Outbound text is now passed through format_message()strip_markdown() so iMessage (plain text) doesn't render raw Markdown. — adapter.py
  • Added _send_with_retry() with exponential backoff for retryable/network errors and a plain-text fallback, so a transient send failure no longer drops the reply. — adapter.py

Group flattening

  • Enabled options: { flattenGroups: true } in the sidecar's Spectrum({...}) config so group conversations normalize correctly. — sidecar/index.mjs

Persist & display phone numbers in status

  • setup stores the operator's phone, assigned iMessage line, and user id in auth.json; hermes photon status surfaces them and auto-refreshes (without provisioning new lines) when they're missing. — cli.py, auth.py

Bump SDK

  • spectrum-ts^1.18.0, and setup now runs npm install spectrum-ts@latest so every setup picks up the newest published SDK rather than staying pinned to the committed lockfile. — sidecar/package.json, sidecar/package-lock.json, cli.py

How to Test

hermes photon setup --phone +15551234567
hermes photon status   # shows operator + assigned iMessage numbers
hermes gateway start --platform photon
  1. Run setup → user is created via the Spectrum API; re-running is idempotent (phone dedup).
  2. hermes photon status shows the operator phone and assigned iMessage line (auto-refreshes if absent).
  3. Have the agent reply with Markdown → it's delivered as clean plain text; a transient send error is retried with backoff.

Automated:

pytest tests/plugins/platforms/photon/ tests/tools/test_send_message_target_parse.py tests/tools/test_send_message_tool.py -q
# 65 passed, 1 skipped

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (rebased on main; 5 commits, no already-merged work)
  • I've run the Photon test scope and all tests pass (65 passed, 1 skipped)
  • I've added tests for my changes (tests/plugins/platforms/photon/test_auth.py)
  • I've tested on my platform: macOS (Darwin)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/) — plugin README + website messaging docs (PHOTON_SPECTRUM_HOST)
  • I've updated cli-config.yaml.example if I added/changed config keys — new env var documented in plugin.yaml
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) — sidecar runs on Node; iMessage targets macOS
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

$ pytest tests/plugins/platforms/photon/ tests/tools/test_send_message_target_parse.py tests/tools/test_send_message_tool.py -q
.................................................................         [100%]
65 passed, 1 skipped in 12.33s

Store operator and assigned iMessage numbers in `auth.json` after
setup, and surface them in `hermes photon status`. When numbers are
missing, status auto-refreshes from the dashboard without provisioning
new lines.
Switch `list_users`, `find_user_by_phone`, `create_user`,
`register_user_if_absent`, and `refresh_user_numbers` from the
Dashboard API (Bearer token) to the Spectrum API (Basic auth with
project credentials). Update response unwrapping to handle the nested
`data.users` envelope returned by Spectrum, add `_spectrum_host()`
resolver, `_basic()` header helper, and structured error helpers.
Update tests, docs, and plugin.yaml accordingly.
@underthestars-zhy
underthestars-zhy requested a review from a team June 9, 2026 04:21
Extend the sidecar and Python adapter to handle `voice` content
alongside `attachment`. Voice notes are inlined as base64 (same
size-cap logic), surfaced as `MessageType.VOICE`, and include an
optional `duration` field in fallback markers when bytes are
unavailable.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins platform/email Email (IMAP/SMTP) adapter labels Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have platform/email Email (IMAP/SMTP) adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants