feat(imessage/spectrum by photon): Burrow over iMessage — proactive alerts + two-way assistant#249
Open
caezium wants to merge 8 commits into
Open
feat(imessage/spectrum by photon): Burrow over iMessage — proactive alerts + two-way assistant#249caezium wants to merge 8 commits into
caezium wants to merge 8 commits into
Conversation
…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)
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
claudeCLI, 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
moengine (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)src/llm.ts): OpenAI-compat + Anthropic tool-use loops,selectProviderdispatch,claude-clidelegation.src/burrow-tools.ts): Burrow's read-only tools as LLM specs; allowlist enforced twice (advertised + refused in exec) —clean/purge/uninstall/analyzecan never run.src/safety.ts): owner-only auth (also the reply-loop guard), rolling-window rate limiter, reply cap. Plus single-flight + metadata-only audit log inagent.ts.src/config.ts):resolveLLMreadsBURROW_LLM_*env → config →claude-cli, so the Swift app drives the provider purely via env.check.ts): disk / sustained-CPU / weekly-digest with hysteresis+cooldown debounce (src/alertengine.ts), reusing Burrow's MCP-computed signals.src/onboarding.ts,src/setup.ts): use-cases copy + Photon device-code project creation.Native Swift —
macos/IMessageSidecar.swift: pureSidecarLaunchenv/args builder (XCTest'd inIMessageSidecarTests.swift) + a self-containedProcesssupervisor (restart-on-crash agent + interval check). Config → env, never writes a JSON file.Store.swift: new keys; Photon secret + LLM key in Keychain (mirrorsaiOpenAIKey).SettingsView.swift: a "Burrow over iMessage" section (enable, number, Photon id/secret, agent toggle, provider picker, model/key).AppDelegate.swift: registered instartServices(), gated onStore.iMessageEnabled(opt-in), torn down on terminate.scripts/bundle-sidecar.sh+project.ymlpost-build stage bun+JS intoResources/sidecar/and codesign the binary (mirrorsbundle-engine.sh).Testing
cd tools/burrow-alerts && bun test→ 23 pass (brain, safety, debounce, tools, onboarding, setup, config).IMessageSidecarTestsruns in CI.xcodegen generatevalidatesproject.yml.xcodebuildcompile + GUI hand-test (my sandbox can't runxcodebuild test, and the dev machine's disk was near-full — a build writes GBs). Needs a localscripts/build.sh+ hand-test before merge.Notes
tools/burrow-alerts/FRICTION.md(F1–F7).config.local.json/node_modulesare gitignored (not in this PR).