Skip to content

fix(photon): harden iMessage outbound recovery - #51075

Closed
mattshapsss wants to merge 3 commits into
NousResearch:mainfrom
mattshapsss:fix/photon-outbound-recovery
Closed

mattshapsss wants to merge 3 commits into
NousResearch:mainfrom
mattshapsss:fix/photon-outbound-recovery

Conversation

@mattshapsss

@mattshapsss mattshapsss commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • resolves Photon DM space ids with space.get() before falling back to phone-based create, so established any;-;+E164 iMessage DMs stay sendable after sidecar restart
  • upgrades the Photon sidecar to spectrum-ts@5.2.0 and makes the mixed-attachment patch script explicitly skip the newer inbound mapper instead of failing startup/install
  • returns sanitized machine-readable sidecar errors (code, retryable, error) so Hermes retries only classified transient Spectrum reconnect failures and does not spin on permanent auth/project-policy failures
  • propagates sidecar retry classification through normal gateway sends, attachment sends, and standalone hermes send
  • transcodes inbound iPhone HEIC/HEIF images to JPEG before handing them to model vision APIs, avoiding non-retryable invalid image provider failures
  • expands Photon regression coverage for retryability, non-retryable project-policy failures, sidecar classification, DM resolution ordering, and HEIC image handling

Tests

  • python -m pytest tests/gateway/test_photon_plugin.py -q -o 'addopts=' → 7 passed
  • python -m py_compile plugins/platforms/photon/adapter.py tests/gateway/test_photon_plugin.py
  • cd plugins/platforms/photon/sidecar && node --check index.mjs && node --check patch-spectrum-mixed-attachments.mjs && node patch-spectrum-mixed-attachments.mjs
  • git diff --check origin/main...HEAD

Live dogfood verification

  • refreshed Photon credentials and reloaded the gateway under launchd
  • verified Photon state returned to connected
  • verified outbound delivery: hermes send --to photon --json ... returned real Spectrum message ids
  • verified inbound delivery by sending an iMessage to the assigned Photon number; gateway logged inbound message: platform=photon ... msg='Hermes inbound verification probe 19:55:48'
  • reproduced the later failure as a provider-side invalid-image rejection for an inbound .heic screenshot, then verified that the same HEIC bytes now cache as JPEG locally
  • verified the gateway/provider path after reload with an inbound Photon text probe; Hermes replied provider ok

Risk

Medium: Photon/iMessage sidecar dependency, outbound error semantics, and macOS-only HEIC conversion via sips. Retry behavior remains bounded; permanent auth/project-policy failures are now surfaced as non-retryable instead of being retried as generic sidecar 500s. If HEIC conversion is unavailable, the attachment falls back to document caching rather than being dropped.

@alt-glitch alt-glitch added type/bug Something isn't working comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have labels Jun 22, 2026
@huntsyea

Copy link
Copy Markdown
Contributor

Cherry-picked this locally on top of the current Photon stack (spectrum-ts@5.2.0) because it addresses the next most likely failure after sidecar restart/re-auth: outbound send recovery during transient Photon/Spectrum auth or reconnect windows.

Local validation passed:

python3 -m py_compile plugins/platforms/photon/adapter.py
node --check plugins/platforms/photon/sidecar/index.mjs
python3 -m pytest tests/gateway/test_photon_plugin.py -q -o 'addopts='
# 3 passed

Best-practice note from dogfooding: bounded retry is the right shape here, but keeping permanent auth/config failures distinct from transient stream/reconnect failures is important. During today’s incident we saw both SpectrumCloudError: Invalid credentials/401 and upstream CatchUpEvents connection failures; conflating those would make retry behavior harder to reason about.

@mattshapsss
mattshapsss force-pushed the fix/photon-outbound-recovery branch from 4462a40 to 9dd306c Compare June 22, 2026 23:59
@mattshapsss
mattshapsss requested a review from a team June 22, 2026 23:59
@mattshapsss

Copy link
Copy Markdown
Contributor Author

Thanks — I pushed an updated version that incorporates this distinction directly. The sidecar now returns sanitized code/retryable classifications, Python preserves that through SendResult, and retry is limited to classified transient Spectrum reconnect cases. Permanent auth/project-policy failures like Target not allowed, invalid credentials/401, and persistent upstream auth failures no longer get treated as generic retryable 500s.

Also rebased onto current origin/main, upgraded the sidecar to spectrum-ts@5.2.0, and verified with 6 Photon regression tests plus live dogfood: outbound hermes send --to photon returned real message ids and an inbound iMessage probe reached the gateway.

@mattshapsss

Copy link
Copy Markdown
Contributor Author

Follow-up dogfood found one more real break: inbound iPhone screenshots arrived as HEIC and the model provider rejected them as invalid image data. I pushed another commit that transcodes Photon inbound HEIC/HEIF attachments to JPEG via macOS sips before the gateway forwards them to vision APIs, with a regression test.

Validation now: 7 Photon tests pass; the actual cached HEIC from the failed iMessage converts to JPEG locally; after gateway reload, an inbound Photon text probe reached the model and Hermes replied provider ok.

@teknium1

Copy link
Copy Markdown
Collaborator

Thanks for the detailed recovery work. The core outbound premise remains present on current main: plugins/platforms/photon/sidecar/index.mjs:661-676 still creates a phone DM before attempting space.get(), and index.mjs:600-606 collapses all sidecar exceptions to internal sidecar error, which the adapter treats as retryable at adapter.py:97-104.

Problems

  • The branch's Spectrum 5 dependency/patch changes are stale. Current main pins Spectrum 8 at plugins/platforms/photon/sidecar/package.json:16; its required mixed-attachment patch targets @spectrum-ts/imessage at patch-spectrum-mixed-attachments.mjs:118-162. The PR's major >= 5 early-return would bypass that current patch.
  • The proposed HEIC conversion invokes synchronous subprocess.run(..., timeout=15) while caching inbound media. That can block the async gateway receive path. Current main already performs provider-format compatibility routing in agent/image_routing.py:651-667.

Suggested changes

  • Selectively salvage DM get-before-create and structured retry classification against the current Spectrum 8 sidecar, retaining its existing mapper patch.
  • Add behavior tests under tests/plugins/platforms/photon/ and avoid synchronous conversion on the gateway event loop.

Automated hermes-sweeper review.

@alt-glitch alt-glitch added the sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages label Jul 15, 2026
@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
@alt-glitch alt-glitch added platform/whatsapp WhatsApp Business adapter and removed sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows platform/whatsapp WhatsApp Business adapter labels Jul 15, 2026
@teknium1

Copy link
Copy Markdown
Collaborator

Closing — the salvageable pieces (generic-500-as-retryable classification) landed via #73563's structured error classes, and the zombie-stream detection via #73615. The Spectrum 5 dep/patch changes would conflict with main's required Spectrum-8 patch, and sync sips HEIC conversion duplicates agent/image_routing. The DM get-before-create idea is noted for a follow-up. Thanks — parts of this PR shaped the final design.

@teknium1 teknium1 closed this Jul 29, 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 sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants