Skip to content

Resolve Tahoe fallback service from chat metadata - #832

Draft
qayshp wants to merge 6 commits into
BlueBubblesApp:developmentfrom
qayshp:agent/fix-tahoe-any-applescript
Draft

Resolve Tahoe fallback service from chat metadata#832
qayshp wants to merge 6 commits into
BlueBubblesApp:developmentfrom
qayshp:agent/fix-tahoe-any-applescript

Conversation

@qayshp

@qayshp qayshp commented Jul 23, 2026

Copy link
Copy Markdown

Summary

  • resolve Tahoe any chat GUIDs to a concrete Messages service from the matching chat's metadata before generating the address-based AppleScript fallback
  • accept only iMessage, SMS, or RCS as resolved services and fail closed when the lookup fails, returns no matching service, or returns an unsupported value
  • keep service-lookup failure diagnostics generic instead of logging arbitrary database exception text
  • preserve explicit service GUID prefixes, the unqualified-address iMessage default, and the primary chat-ID send path
  • add focused regression coverage for service resolution, failure handling, and the ActionHandler fallback wiring

Root cause

On macOS 26 Tahoe, any is a transport-agnostic chat GUID prefix observed across iMessage, SMS, and RCS chats. Messages AppleScript still requires a concrete service type, so interpolating any into the fallback script raises AppleScript error -1700. A hardcoded or unresolved anyiMessage mapping could misroute a non-iMessage conversation.

Implementation

When the primary chat-ID send fails for an any;... GUID, ActionHandler queries the exact matching chat without loading participants and passes its serviceName to sendMessageFallback(). The script generator:

  • honors explicit iMessage, SMS, and RCS GUID prefixes
  • uses a resolved iMessage, SMS, or RCS value for any GUIDs
  • throws a clear service-resolution error for an unresolved or unsupported any service before any fallback AppleScript is executed
  • retains the existing iMessage default for unqualified addresses

If the metadata lookup itself fails, the debug log records only the generic failure decision, not the underlying exception text.

Validation

Passed on the current head:

  • node --test test/tahoeFallbackService.test.cjs — 6/6 tests
  • targeted ESLint
  • targeted Prettier for the newly changed code; the unchanged quote style in scripts.ts still matches the base branch
  • git diff --check

The focused suite covers:

  • resolved any iMessage, SMS, and RCS selection through both the script generator and ActionHandler
  • authoritative explicit service GUIDs and proof that they bypass chat-metadata lookup
  • the unqualified-address iMessage default
  • lookup exceptions, empty results, null services, and unsupported services
  • exact public errors, privacy-safe debug and warning logs, and proof that failure cases do not execute fallback AppleScript

Live validation on a SIP-enabled Tahoe host used the current production implementation without publishing identifiers or message content:

  • a resolved any iMessage route recorded outgoing, sent, delivered, and no error
  • a resolved any SMS route recorded outgoing, sent, and no error; no delivery receipt was available, so this is a dispatch pass
  • a resolved any RCS route recorded outgoing, sent, delivered, and no error

Each live run forced the primary chat-ID path to fail, performed one exact-chat metadata lookup, executed one correctly resolved address-based fallback, and created one matching outgoing record. The failure-only cases are covered deterministically by the focused suite; no Messages database fault injection was needed. No recipient-confirmed delivery is claimed.

The production build reaches only the existing unrelated ScheduledService.ts:39 Timer/Timeout type error.

Fixes #777

@qayshp qayshp left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review focused on the remaining service-selection assumption and its coverage.

Comment thread packages/server/src/server/api/apple/scripts.ts Outdated
@qayshp

qayshp commented Jul 23, 2026

Copy link
Copy Markdown
Author

Validation update

Validated on the current SIP-enabled Tahoe host:

  • Passed: generated-AppleScript assertions for normalizing the Tahoe any service prefix to iMessage.
  • Explicit iMessage, SMS, and RCS prefixes remained unchanged, an unqualified address retained the existing iMessage default, and the primary chat-ID script retained its original GUID.
  • Passed read-only: Messages exposed an iMessage account to AppleScript; the account identifier was discarded and no send command was issued.

No message was sent. The exact address-fallback path supports only one-to-one chats and rejects group chats, so it cannot be exercised in the user-designated two-recipient conversation without testing a different code path. This remains an end-to-end validation gap.

@qayshp qayshp changed the title Fix Tahoe AppleScript fallback for any service GUIDs Resolve Tahoe fallback service from chat metadata Jul 27, 2026
@qayshp

qayshp commented Jul 27, 2026

Copy link
Copy Markdown
Author

Superseding the earlier validation note: the final implementation no longer hardcodes Tahoe any GUIDs to iMessage. It resolves the matching chat's service, allow-lists iMessage/SMS/RCS, and retains the legacy iMessage default only when resolution is unavailable. The focused suite passes 4/4 on the current head. Live Tahoe fallback sends recorded correct iMessage and RCS delivery; the SMS-only route recorded service=SMS, outgoing/sent=true, and error=0. No SMS delivery receipt was available, so recipient-confirmed SMS delivery is not claimed.

@qayshp

qayshp commented Jul 28, 2026

Copy link
Copy Markdown
Author

Latest-head validation (bf9e8d20):

  • Removed unrelated quote-style churn from scripts.ts.
  • Service-lookup failures now log only the generic fallback decision; the regression check proves arbitrary exception text is not emitted and the legacy iMessage default is still used.
  • The focused suite passes 4/4 under Node 20; targeted ESLint, changed-file Prettier, and git diff --check pass. The unchanged scripts.ts quote style matches the base branch.
  • The production build reaches only the unchanged ScheduledService.ts:39 type error already present on the base branch.
  • A sequential merge simulation against current origin/development with Server Fix headless startup window minimization #831, Add typed, privacy-safe Google contact sync logging #833, and Restore legacy macOS update manifest generation #834 completed without conflicts.

The previously recorded privacy-minimized live iMessage, RCS, and SMS dispatch evidence remains applicable because these follow-ups do not change service selection.

@qayshp
qayshp marked this pull request as ready for review July 28, 2026 07:02
@qayshp
qayshp marked this pull request as draft July 28, 2026 07:30

@ruinthemagic ruinthemagic left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent validation on a SIP-enabled macOS 26.5.2 host confirms that the Tahoe any GUID prefix is transport-agnostic: it occurs on both SMS and iMessage chats. No identifiers or message content were inspected. Exact chat GUIDs are unique here, and every any-prefixed row inspected has a supported, non-null service_name, so resolving the transport from the exact chat metadata is the correct design. The focused 4-case suite and targeted ESLint pass locally; the production build reaches only the unrelated pre-existing ScheduledService Timer/Timeout error. One remaining concern: when resolution fails or returns an unsupported value, this PR defaults to iMessage. That is not truly legacy behavior for any GUIDs—the current code errors—and it could silently switch an SMS or RCS conversation to iMessage. Please consider failing closed with a clear service-resolution error instead. That should preserve transport correctness without affecting normal Tahoe chats, whose metadata resolves successfully.

@qayshp qayshp left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Full-diff self-review found no blocking correctness changes. One non-blocking coverage note: the focused 6-case suite is exposed as a package script but is not invoked by a root test command or GitHub Actions, so regression coverage remains manual. I would prefer wiring it into an appropriate macOS CI job when the repository has one, but I would not block this PR on introducing new CI infrastructure.

@@ -91,7 +91,23 @@ export class ActionHandler {
try {
// Generate the new send script
log.debug(`Sending AppleScript text using fallback script...`);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: service resolution can now fail before a fallback script is generated or executed, so this log may say the fallback is being sent when none will be. Consider wording it as 'Preparing AppleScript text fallback...' so the logs distinguish preparation from execution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants