Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5870ee4
build(mobile): iOS 27 SDK enablement — scene lifecycle, entitlement +…
ddutchie Jul 8, 2026
fb818f6
feat(mobile): Apple Intelligence via Private Cloud Compute (iOS 27)
ddutchie Jul 8, 2026
8b2274b
feat(mobile): tab bar UX for iOS 27 — search placement, keyboard offs…
ddutchie Jul 8, 2026
72b7688
fix(mobile): match search empty state position to chat on iOS 27
ddutchie Jul 8, 2026
fea3ba9
Merge branch 'main' into ddutchie/ios27
ddutchie Jul 8, 2026
3223173
Merge branch 'main' into ddutchie/ios27
ddutchie Jul 8, 2026
1f1efc7
feat(mobile): context-window usage ring in chat (Apple provider)
ddutchie Jul 9, 2026
444320b
feat(mobile): 3-state PCC usage bar in AI settings
ddutchie Jul 9, 2026
32074b6
feat(mobile): context ring for all providers + header-left placement
ddutchie Jul 9, 2026
d432e9f
feat(mobile): show context-window size in Apple + Rork provider notes
ddutchie Jul 9, 2026
d1a507f
Merge branch 'main' into ddutchie/ios27
ddutchie Jul 9, 2026
681505a
PR Review
ddutchie Jul 9, 2026
5b3e92e
feat(mobile): user-selectable PCC reasoning effort
ddutchie Jul 9, 2026
02ccf53
feat(mobile): stream + render PCC reasoning
ddutchie Jul 9, 2026
c3c720a
Merge branch 'main' into ddutchie/ios27
ddutchie Jul 9, 2026
6ae4732
accessibilityState fix
ddutchie Jul 9, 2026
3f3fe35
feat(mobile): stream reasoning from OpenAI-compatible endpoints
ddutchie Jul 9, 2026
fabd9d5
Merge branch 'main' into ddutchie/ios27
ddutchie Jul 9, 2026
e9e3265
fix(mobile): drop Apple PCC reasoning extraction (redacted, not usable)
ddutchie Jul 9, 2026
a6206af
feat(mobile): present Sync + AI settings as native formSheet
ddutchie Jul 9, 2026
b60116e
fix(mobile): revert Sync/AI settings to modal; unify modal backgrounds
ddutchie Jul 9, 2026
f951520
style(mobile): unify New Task modal surfaces with Sync/AI settings
ddutchie Jul 9, 2026
5d76d2a
feat(mobile): context ring opens a native menu instead of an Alert
ddutchie Jul 9, 2026
5540c09
feat(mobile): reusable GlassMenu; use it for context ring + image picker
ddutchie Jul 9, 2026
36b3e75
style(mobile): drop chat composer border when Liquid Glass is active
ddutchie Jul 9, 2026
6723e13
feat(mobile): long-press context menu on note rows
ddutchie Jul 9, 2026
a556704
Revert "feat(mobile): long-press context menu on note rows"
ddutchie Jul 9, 2026
f8b500e
fix(mobile): don't render redacted reasoning as a thinking block
ddutchie Jul 9, 2026
dcdc6a7
feat(sync): workspace-scoped oplogs + multi-source mobile
ddutchie Jul 10, 2026
e902752
Merge branch 'main' into ddutchie/ios27
ddutchie Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 56 additions & 4 deletions docs/plans/apple-foundation-models-provider.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,61 @@
# Plan: On-device / Private Cloud Compute Apple Intelligence provider

**Status:** shelved. On-device (iOS 26) implementation exists in the repo but is
**not viable** for Cairn's agentic tool use. Re-implement against **Private Cloud
Compute (PCC)** once the managed entitlement is granted and an **iOS 27 SDK** is
available.
**Status:** PCC IMPLEMENTED (2026-07-08). Private Cloud Compute is now the
user-facing "Apple Intelligence" provider (full tool set, full system prompt,
32K window, `moderate` reasoning, quota UI). The on-device (iOS 26) model is
retained but DEV-ONLY behind `EXPO_PUBLIC_APPLE_LLM_DEV` (too weak for agentic
tool use). Verified: JS type-check + lint clean; Swift parses clean under BOTH
the stable iOS-26 SDK (PCC compiled out) and the beta iOS-27 SDK (PCC compiled
in). Pending device verification on iOS 27 with the granted entitlement.

### EAS-safety: the CAIRN_PCC_SDK compile-time gate (critical)

PCC symbols (`PrivateCloudComputeLanguageModel`, `ContextOptions.ReasoningLevel`,
`.quotaLimitReached`, `quotaUsage`) exist ONLY in the iOS 27 SDK. `#available(iOS
27)` is a RUNTIME check — it does NOT stop the compiler needing those symbols to
exist. `#if canImport(FoundationModels)` is TRUE on both SDK 26 and 27 (only the
PCC types within it are new), so it can't gate them either. The current EAS iOS
image is `macos-tahoe-26.4-xcode-26.4` (iOS 26 SDK) — referencing PCC symbols
unconditionally there FAILS the build.

Fix (`modules/apple-llm/ios/AppleLlm.podspec`): read the active iOS SDK version
at pod-eval time (`xcrun --sdk iphoneos --show-sdk-version`) and define the
`CAIRN_PCC_SDK` compilation condition ONLY when the SDK major is ≥ 27. ALL PCC
symbol references live inside `#if CAIRN_PCC_SDK`. Result:
- EAS / stable Xcode 26 (iOS 26 SDK) → flag off → PCC compiled out → builds green,
PCC reports unavailable at runtime.
- Local Xcode 27 beta (`/Applications/Xcode-beta.app`, iOS 27 SDK) → flag on →
PCC compiled in + runs. Point the toolchain at it:
`sudo xcode-select -s /Applications/Xcode-beta.app/Contents/Developer`
(or prefix a build with `DEVELOPER_DIR=/Applications/Xcode-beta.app/Contents/Developer`).
- Future EAS iOS 27 image → flips on automatically, zero code change.

### SDK API gotcha: LanguageModelSession init + `LanguageModel` are iOS-27-only

Verified against the iOS 27 SDK `.swiftinterface` (build error surfaced it):
- The **unified** `LanguageModelSession.init(model: some LanguageModel, tools:, instructions:)` and the `LanguageModel` protocol itself are **iOS 27.0+**. Passing a `PrivateCloudComputeLanguageModel` REQUIRES this init.
- iOS 26 only has the **concrete** `init(model: SystemLanguageModel = .default, tools:, instructions:)`.
- So there is NO single shared init/`resolveModel(): any LanguageModel` (that annotation compiled under `@available(iOS 26)` but references 27-only symbols → error). `ensureSession` must branch: server path (`#if CAIRN_PCC_SDK` + `#available(iOS 27)`) builds with `PrivateCloudComputeLanguageModel()` via the `some LanguageModel` init; on-device path uses the concrete-`SystemLanguageModel` init.
- Likewise `streamResponse(to:options:contextOptions:)` is iOS 27+ (base `streamResponse(to:options:)` is iOS 26); reasoning is applied only on the server path.
- Quota/availability shapes confirmed: `PrivateCloudComputeLanguageModel()`, `.availability → .available/.unavailable(.deviceNotEligible|.systemNotReady)`, `.quotaUsage.status → .belowLimit(BelowLimit{isApproachingLimit})`, `.isLimitReached`, `.limitIncreaseSuggestion?.show()`, `.resetDate`, `Error.quotaLimitReached(_)`.
- **Verified:** AppleLlm pod `xcodebuild BUILD SUCCEEDED` under iOS 27 SDK (flag on); parses clean under iOS 26 SDK (flag off).

### Entitlement gating (critical)

`com.apple.developer.private-cloud-compute` is a MANAGED entitlement
without it being provisioned for the App ID BREAKS code-signing on every build.
`plugins/withPrivateCloudCompute.js` therefore injects it ONLY when
`EXPO_PUBLIC_PCC=1`. Set that flag only once the entitlement is granted AND you
build the iOS 27 SDK variant. The app runs fine without it (PCC just reports
unavailable), so shipped/EAS builds stay signable.

---

## Original shelving context (on-device)

On-device (iOS 26) implementation exists but is **not viable** for Cairn's
agentic tool use. Re-implemented against **Private Cloud Compute (PCC)** — see
status above.

---

Expand Down
34 changes: 24 additions & 10 deletions mobile/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,28 @@ EXPO_PUBLIC_TOOLKIT_URL=
# ─────────────────────────────────────────────────────────────────────────────
# Apple on-device AI (experimental, dev-only)
# ─────────────────────────────────────────────────────────────────────────────
# The Apple Foundation Models (on-device Apple Intelligence) provider is
# EXPERIMENTAL and not yet viable for Cairn's agentic tool use — the on-device
# model is too small / context too tight (see
# docs/plans/apple-foundation-models-provider.md; the plan is to redo it against
# Private Cloud Compute on iOS 27). It is therefore HIDDEN from end users.
#
# Set to 1 in your LOCAL .env.local to expose the "On-device" provider in
# Settings → AI so you can keep iterating on a device. Leave BLANK/unset for EAS
# and any shipped build — the provider then never appears or resolves. (The
# native module still compiles but stays dormant unless this flag is on.)
# The on-device Apple Foundation Models model is EXPERIMENTAL and not viable for
# Cairn's agentic tool use — the model is too small / context too tight (fixed
# 4096 tokens). The USER-FACING Apple provider is Private Cloud Compute (below),
# NOT this. The on-device model is HIDDEN from end users.
#
# Set to 1 in your LOCAL .env.local to expose the on-device model in Settings →
# AI so you can keep iterating. Leave BLANK/unset for EAS and shipped builds.
# (The native module still compiles but stays dormant unless this flag is on.)
EXPO_PUBLIC_APPLE_LLM_DEV=

# ─────────────────────────────────────────────────────────────────────────────
# Apple Intelligence via Private Cloud Compute (iOS 27+)
# ─────────────────────────────────────────────────────────────────────────────
# PCC is the USER-FACING Apple provider: 32K context, stronger reasoning, no API
# key, privacy-preserving. It only compiles into the app when built against the
# iOS 27+ SDK (the AppleLlm podspec auto-defines CAIRN_PCC_SDK from the active
# SDK), and only runs on an eligible iOS 27+ device — so it's a no-op on the
# current EAS image (iOS 26 SDK) and simply reports unavailable.
#
# Set to 1 ONLY once the managed entitlement
# `com.apple.developer.private-cloud-compute` is granted for the App ID AND you
# build with the iOS 27 SDK (Xcode 27). This makes withPrivateCloudCompute inject
# the entitlement. Leave BLANK otherwise — adding an unprovisioned managed
# entitlement breaks code-signing on every build.
EXPO_PUBLIC_PCC=
11 changes: 11 additions & 0 deletions mobile/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
],
"./plugins/withAppleSigning",
"./plugins/withICloudContainer",
"./plugins/withPrivateCloudCompute",
"./plugins/withSceneLifecycle",
"./plugins/withPodDeploymentTarget",
"./plugins/withStatusBarStyle",
"@react-native-community/datetimepicker",
"expo-secure-store",
Expand All @@ -52,6 +55,14 @@
"backgroundColor": "#0d0d0d"
}
}
],
[
"expo-build-properties",
{
"ios": {
"deploymentTarget": "16.4"
}
}
]
],
"experiments": {
Expand Down
12 changes: 7 additions & 5 deletions mobile/app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,17 @@ export default function TabsLayout() {
<NativeTabs.Trigger.Label>Graph</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>

<NativeTabs.Trigger name="search" role="search">
<NativeTabs.Trigger.Icon sf="magnifyingglass" md="search" />
<NativeTabs.Trigger.Label>Search</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>

<NativeTabs.Trigger name="chat">
<NativeTabs.Trigger.Icon sf="bubble.left.and.bubble.right.fill" md="chat" />
<NativeTabs.Trigger.Label>Chat</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>

{/* Search last so it sits on the far right — matches the iOS search-tab
convention and keeps it out of the primary nav flow. */}
<NativeTabs.Trigger name="search" role="search">
<NativeTabs.Trigger.Icon sf="magnifyingglass" md="search" />
<NativeTabs.Trigger.Label>Search</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
</NativeTabs>
</ThemeProvider>
);
Expand Down
Loading
Loading