Skip to content

fix(photon): handle inbound richlinks and fall back on outbound enable_data_detection errors - #54406

Closed
cacheburner-agent wants to merge 3 commits into
NousResearch:mainfrom
cacheburner-agent:cacheburner-agent/fix-photon-inbound-outbound
Closed

cacheburner-agent wants to merge 3 commits into
NousResearch:mainfrom
cacheburner-agent:cacheburner-agent/fix-photon-inbound-outbound

Conversation

@cacheburner-agent

Copy link
Copy Markdown

Summary

Fixes two separate bugs in the Photon sidecar related to iMessage rich links — one inbound, one outbound.

Bug 1 (inbound): When someone sends a link via iMessage, Apple renders it as a rich link balloon. spectrum-ts converts this to a richlink content object with the URL in content.url, but the sidecar's normalizeContent() didn't handle this type — it fell through to the generic fallback, dropping the URL. The Python adapter received an empty message.

Fix: Added a richlink case to normalizeContent() that extracts the URL as plain text.

Bug 2 (outbound): When Hermes sends a message containing a URL with a protocol prefix, spectrum-ts sets enable_data_detection: true on the gRPC payload. Apple's IMAgentKit rejects this field with a ValidationError, silently dropping the message with HTTP 500.

Fix: Wrapped space.send() in a try/catch in the /send handler. When the error message contains enable_data_detection, the text is retried with protocol prefixes stripped. Without a protocol prefix, spectrum-ts's link detector doesn't fire, data detection stays off, and the message delivers. iOS still renders domain-like text as a tappable link on the receiving end. Non-data-detection errors are re-thrown unchanged.

An alternative approach for Bug 2 would be patching spectrum-ts's dataDetectionOption function at postinstall time (via the existing patch-spectrum-mixed-attachments.mjs infrastructure) to always return {}. That would be more aggressive — it disables data detection for all messages, not just on failure. The fallback approach in this PR is less invasive: it preserves data detection when it works and only strips it when Apple rejects the message.

Closes #54402

Test Plan

  • Send a link (e.g. https://example.com) to Hermes via iMessage — inbound message should contain the URL as text
  • Ask Hermes to send a link (e.g. https://example.com) via iMessage — message should deliver successfully
  • Send/receive messages without links — should work unchanged
  • Verify non-data-detection errors still propagate correctly

…malizeContent

When someone sends a link via iMessage, Apple's client renders it as a
rich link balloon (com.apple.messages.URLBalloonProvider). spectrum-ts
inbound mapper converts this to a 'richlink' content object with the URL
in content.url. However, the sidecar's normalizeContent did not handle
the 'richlink' type — it fell through to the generic fallback which
returned { type: "richlink" } without the URL, causing the Python
adapter to receive an empty/unknown message.

Fix by adding a 'richlink' case to normalizeContent that extracts the
URL as plain text, so the inbound message arrives as a regular text
message containing the link.
…nable_data_detection

spectrum-ts enables enable_data_detection when outbound text contains
links, triggering Apple's link-preview pipeline. Apple's IMAgentKit
send path rejects this field with a ValidationError, silently dropping
the message.

Fix by catching the error in the /send handler and retrying with
protocol prefixes (http://, https://) stripped. Without a protocol
prefix, spectrum-ts's markdown link detector does not fire, data
detection stays off, and the message delivers. iOS still renders
domain-like text as a tappable link on the receiving end. When the
error is unrelated to data detection, the original error is re-thrown
so existing error handling paths work unchanged.
…a-detection fallback

Adds two static assertion tests matching the existing pattern in
test_spectrum_patch.py:

- test_sidecar_normalize_content_handles_richlink: verifies the sidecar's
  normalizeContent handles the 'richlink' content type and extracts the URL
- test_sidecar_send_falls_back_on_enable_data_detection_error: verifies the
  /send handler catches enable_data_detection errors and retries with
  protocol prefixes stripped
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jun 28, 2026
@cacheburner-agent

Copy link
Copy Markdown
Author

Closing because this is already fixed, see the linked issue: #54402

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: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.

[Bug] Photon: inbound rich links arrive as empty messages; outbound messages with URLs silently fail with HTTP 500

2 participants