Skip to content

feat(imessage/spectrum by photon): Burrow over iMessage — proactive alerts + two-way assistant#249

Open
caezium wants to merge 8 commits into
mainfrom
feat/imessage-native
Open

feat(imessage/spectrum by photon): Burrow over iMessage — proactive alerts + two-way assistant#249
caezium wants to merge 8 commits into
mainfrom
feat/imessage-native

Conversation

@caezium

@caezium caezium commented Jul 9, 2026

Copy link
Copy Markdown
Owner

What

Your Mac texts you when disk/CPU cross a threshold, and — with the assistant on — answers when you text it. Built for a headless Mac you can't walk over to (a Mac Studio, a build box). Delivery runs on Photon spectrum-ts (cloud; free for one user). The two-way assistant uses a bring-your-own-key LLM (OpenRouter / OpenAI-compatible / Anthropic) or your local claude CLI, restricted to read-only Burrow tools.

How it's shaped

The Burrow app bundles + configures + supervises a TypeScript sidecar — the same pattern used for the mo engine (spectrum-ts is TS, so it ships as a bundled sidecar, not a Swift rewrite). Swift owns the UI/setup/lifecycle; the sidecar does delivery + inbound + the LLM brain.

Sidecar — tools/burrow-alerts/ (TypeScript/Bun, 23 unit tests)

  • Provider-flexible brain (src/llm.ts): OpenAI-compat + Anthropic tool-use loops, selectProvider dispatch, claude-cli delegation.
  • Read-only tool bridge (src/burrow-tools.ts): Burrow's read-only tools as LLM specs; allowlist enforced twice (advertised + refused in exec) — clean/purge/uninstall/analyze can never run.
  • Safety (src/safety.ts): owner-only auth (also the reply-loop guard), rolling-window rate limiter, reply cap. Plus single-flight + metadata-only audit log in agent.ts.
  • Config (src/config.ts): resolveLLM reads BURROW_LLM_* env → config → claude-cli, so the Swift app drives the provider purely via env.
  • Alerts (check.ts): disk / sustained-CPU / weekly-digest with hysteresis+cooldown debounce (src/alertengine.ts), reusing Burrow's MCP-computed signals.
  • Onboarding + setup (src/onboarding.ts, src/setup.ts): use-cases copy + Photon device-code project creation.

Native Swift — macos/

  • IMessageSidecar.swift: pure SidecarLaunch env/args builder (XCTest'd in IMessageSidecarTests.swift) + a self-contained Process supervisor (restart-on-crash agent + interval check). Config → env, never writes a JSON file.
  • Store.swift: new keys; Photon secret + LLM key in Keychain (mirrors aiOpenAIKey).
  • SettingsView.swift: a "Burrow over iMessage" section (enable, number, Photon id/secret, agent toggle, provider picker, model/key).
  • AppDelegate.swift: registered in startServices(), gated on Store.iMessageEnabled (opt-in), torn down on terminate.
  • Bundling: scripts/bundle-sidecar.sh + project.yml post-build stage bun+JS into Resources/sidecar/ and codesign the binary (mirrors bundle-engine.sh).

Testing

  • Sidecar: cd tools/burrow-alerts && bun test23 pass (brain, safety, debounce, tools, onboarding, setup, config).
  • Swift: IMessageSidecarTests runs in CI. xcodegen generate validates project.yml.
  • Not yet done here: xcodebuild compile + GUI hand-test (my sandbox can't run xcodebuild test, and the dev machine's disk was near-full — a build writes GBs). Needs a local scripts/build.sh + hand-test before merge.

Notes

  • Photon SDK QA findings logged in tools/burrow-alerts/FRICTION.md (F1–F7).
  • Secrets live in Keychain; config.local.json/node_modules are gitignored (not in this PR).

…istant

Native macOS feature: your Mac texts you when disk/CPU cross thresholds and
answers when you text back — made for a headless Mac (e.g. a Mac Studio).
Delivery via Photon spectrum-ts (cloud, free for one user). The two-way
assistant uses a BYO-key LLM (OpenRouter / OpenAI-compatible / Anthropic) or
the local `claude` CLI, restricted to READ-ONLY Burrow tools.

Sidecar (tools/burrow-alerts — TypeScript/Bun, 23 tests):
- provider-flexible brain (src/llm.ts): tool-use loops + selectProvider
- read-only Burrow tool bridge, allowlist enforced twice (src/burrow-tools.ts)
- safety: owner-only auth (+ loop guard), rate limit, reply cap (src/safety.ts)
- env-driven provider config (src/config.ts); onboarding + device-code setup
- disk/CPU/digest alerts with hysteresis+cooldown debounce (check.ts)

Native Swift (macos/):
- IMessageSidecar: pure launch-spec builder (XCTest) + Process supervisor
  (restart-on-crash agent + interval check); config passed via env
- Store keys; Photon secret + LLM key in Keychain (mirrors aiOpenAIKey)
- Settings "Burrow over iMessage" section; AppDelegate registration (opt-in)
- bundle-sidecar.sh + project.yml post-build (mirrors bundle-engine.sh)
@caezium caezium changed the title feat(imessage): Burrow over iMessage — proactive alerts + two-way assistant feat(imessage/spectrum by photon): Burrow over iMessage — proactive alerts + two-way assistant Jul 9, 2026
caezium added 7 commits July 8, 2026 22:15
…cessInfo struct)

CI Swift build failed: 'Type ProcessInfo has no member processInfo' — Burrow's
metrics ProcessInfo (MoleStatus.swift) shadowed Foundation.ProcessInfo.
Disk alerts can render as an iMessage mini-app card (customizedMiniApp): a rich
bubble with caption/subcaption, a lock-screen `summary` fallback, and a
tap-to-open deep link. Opt-in via `card` config (extension identity + URL);
cloud-only, and falls back to plain text in local mode or if the SDK rejects
the card. Pure card builder is unit-tested (src/card.test.ts).
…mand

Sends a sample disk card (no threshold, no MCP) using the configured `card`
block; skips with a hint if `card` is unset or not in cloud mode. Falls back to
text like any card send.
… wire format

New iOS iMessage-extension surface under imessage/. Tracer bullet: the
declarative BurrowLayout card schema on both sides of the wire —
- tools/burrow-alerts/src/burrowlayout.ts: schema + diskLayout builder +
  base64url `?p=` transport (unit-tested round-trip).
- imessage/Shared/Sources/BurrowCards/BurrowLayout.swift: matching Codable
  schema + URL-safe base64 decoder (byte-for-byte with the TS test).

Renderer, MSMessagesAppViewController, host app, and project.yml still to come
(see imessage/README.md status table). Vocabulary derived from HermesShare (MIT).
… end-to-end

Renderer, extension, host-app harness, and xcodegen project. `xcodegen generate`
+ the host scheme compiles clean for iOS Simulator.

- BurrowLayoutRenderer.swift: BurrowLayout → native SwiftUI (vstack/hstack/
  section/text/statusBadge/progressBar/gauge/keyValueRow + action buttons + #RRGGBB).
- MessagesViewController: decode selected message's ?p= → render; compose gallery
  inserts sample cards; action buttons open burrow:// deep links.
- BurrowCardsHost: debug harness — sample picker + live-JSON paste, renders with
  no send. Fastest iteration loop.
- BurrowTransport.encode (Swift) mirrors the sidecar's encodeLayoutURL.
- Sidecar: card.ts now embeds the BurrowLayout as ?p= in the customizedMiniApp url.
- Samples.swift: disk / CPU / cleanup cards. project.yml: host embeds extension.

Test paths + config in imessage/README.md. Generated .xcodeproj/Info.plist gitignored.
Register the `burrow` URL scheme (project.yml → CFBundleURLTypes) and route
`burrow://action?id=…` to the right pane: clean → Clean tool, inspect → Status,
anything else surfaces Home. AppDelegate.pane(forDeepLink:) is pure + unit-tested
(DeepLinkTests). Closes the loop on the extension's action buttons.
Attach a Burrow JPEG (layout.image) + dynamic imageTitle/imageSubtitle so the
collapsed bubble is branded even for recipients without the extension. Image
bytes load from a bundled asset at send time (sender.ts) — card.ts stays pure;
missing asset degrades gracefully to captions/summary only.
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.

1 participant