Nucleic-Session: 93D678C7-1451-4F73-B3CE-67BF8B2EDE02 Co-authored-by: Nucleic <[email protected]>
20 KiB
Nucleic — App Intents Integration Opportunities
Scope. A survey of where Apple's App Intents framework could plug into Nucleic — across both the macOS host app (
Sources/NucleicApp) and the iPhone companion (ios/NucleicRemote). Grounded in the shipping code as of this writing. Every opportunity is tied to an existing action or model type, withfile:lineanchors, so this doubles as an implementation map. Status notes are honest about feasibility and platform constraints.TL;DR. Nucleic is unusually well-suited to App Intents: it is an attention-routing product ("which agent needs me?") whose whole value is answering a small set of high-stakes actions — approve/deny, unblock, start, ship — from wherever you are. That is exactly the surface Siri, Shortcuts, Spotlight, interactive widgets, Control Center, and the Action button are designed to expose. There is no App Intents code today (confirmed: zero
import AppIntents,AppIntent,AppEntity, orAppShortcutin the tree). The domain model is alreadySendable/Codableend-to-end, and anucleic://deep-link scheme already exists, so the groundwork is laid.
1. Why App Intents fits Nucleic
App Intents lets an app expose typed actions (AppIntent), typed data (AppEntity), and typed
choices (AppEnum) to the system so they can be invoked from Siri / Apple Intelligence,
Shortcuts, Spotlight, the Action button, Control Center, interactive Widgets, and Live Activity
buttons — often without launching the app.
Nucleic's product thesis (see NUCLEIC_CONCEPT.md §2–5, docs/UX_IOS.md §1) is that with N
agents running, the bottleneck is you, and the job is to answer "which session needs me, and
let me act on it in one step." That is a near-perfect match for App Intents' strengths:
- Glanceable, one-tap resolution.
docs/UX_IOS.md§1 lists "Glanceable" and "Notification-first" as invariants; §8 already calls for widgets and Live Activities. App Intents is the connective tissue that makes those surfaces actionable, not just informational. - A small, well-typed action vocabulary. Approve, deny, allow-always, send follow-up, start,
interrupt, integrate, discard — each already exists as a method on
RemoteStore(iOS) orAppStore/SessionController(macOS), and each takes stable identifier types. - Rich queryable state. Sessions have a canonical
SessionStatusand aneedsAttentioncomputation — ideal backing for anEntityQuerythat answers "what needs me?" to Siri/Spotlight.
2. Current state — what exists, what doesn't
No App Intents today. A tree-wide search for import AppIntents, AppIntent, AppEntity,
@Parameter, AppShortcut, IntentDescription, and EntityQuery returns no matches in
either the macOS or iOS target.
Bridging surfaces that already exist (these lower the cost of adopting App Intents):
| Surface | Where | Relevance |
|---|---|---|
nucleic:// URL scheme |
ios/.../SessionActivityAttributes.swift (NucleicDeepLink ~94–127); consumed in NucleicRemoteApp.swift:14-15 via .onOpenURL → RemoteStore.handleDeepLink() (~112–118) |
App Intents can openAppWhenRun and route via the same deep-link map; several intents can reuse this verbatim. |
| Live Activity + Dynamic Island | ios/.../SessionLiveActivity.swift, LiveActivityManager.swift, Shared/SessionActivityAttributes.swift |
Live Activities can host interactive Button(intent:) — the fastest path to approve-from-lock-screen without a notification. |
| Interactive notifications | ios/.../Notifications.swift (categories NUCLEIC_APPROVAL_ACTIONABLE, NUCLEIC_ALLOW, NUCLEIC_DENY, ~24–52; handling ~127–148) |
Already a working approve/deny-from-lock-screen path; App Intents would complement (widgets/Siri) rather than replace it. |
| Widgets target | ios/NucleicRemote/NucleicRemoteWidgets/ |
An existing WidgetKit extension — the natural home for an AppIntentConfiguration widget and ControlWidget. |
Stable, Codable/Sendable identifiers |
SessionID, ProjectID, ApprovalID, TodoID (Protocol/CoreIdentifiers.swift) |
Directly usable as AppEntity.id / intent parameter types. |
| macOS notification & Dock hooks | Sources/NucleicApp/NucleicApp.swift:109-117 (status sound, alarm, dock bounce) |
Shows the app already models "needs you" attention events an intent could surface to Spotlight/Siri. |
3. Architectural constraints (read before scoping)
These are the honest gotchas that shape what's feasible and how hard:
-
The iPhone is a thin client with no local authority. Every action is an intent sent over an E2EE channel to the paired Mac (
docs/UX_IOS.md§2, §9;RemoteStoremethods atios/.../Models/RemoteStore.swift). Two consequences for App Intents:- No optimistic execution.
UX_IOS.md§6 mandates that actions require a live channel and must disable rather than silently fail. An App Intent that runs in an extension process (widget/Control Center/Siri) must therefore either (a) establish/verify the channel and report a clean failure viaIntentResult+ a spoken/dialog error when the Mac is unreachable, or (b) fall back toopenAppWhenRunso the app process (which owns the connection) performs it. There is already a precedent:RemoteStore.respondFromNotification()(~124–136) queues an approval for up to ~30 s on a dropped link — an intent can mirror that contract. - Scope gating. Device scope is
.view/.approve/.control(WireMessages.swift,RemoteStore.canControl~180). Intents must respect it: approve/sendInput need.approve; start/merge/discard need.control(whichUX_IOS.md§2 notes is a later capability). Ship theapprove-scope intents first; gatecontrol-scope intents behind the same check the UI uses.
- No optimistic execution.
-
The macOS app is a SwiftPM executable, not an Xcode app project.
Package.swift:60buildsNucleicAppas an.executableproduct (later wrapped into a signed, notarized.appfor Sparkle distribution — seeBUILD.md). App Intents metadata is normally extracted by the Xcode build system'sappintentsmetadataprocessorinto the app bundle. With a SwiftPM executable you must ensure that step runs (e.g. a packaging step that invokes the extractor, or a thin Xcode wrapper target) soAppShortcutsProviderand entities register with the system. This is the single biggest feasibility risk on the macOS side and should be de-risked with a spike before committing to macOS Shortcuts/Spotlight. iOS (NucleicRemote.xcodeproj) is a normal Xcode project and has no such issue. -
Approvals are security-sensitive and risk-tiered.
Risk(Protocol/Approval.swift:5-10:readOnly/write/execute/network/destructive/hostExec/unknown) already drives where an approval can be answered.UX_IOS.md§5.1 andUX_MACOS.md§4 forbid inline "Allow" ondestructive/network/hostExec— those must open the app behind a biometric gate. Any approve-style App Intent must honor this: expose inline Allow only for low/medium risk; for high risk, the intent shouldopenAppWhenRunto the guarded card..allowAlwaysis likewise never offered on destructive requests (UX_MACOS.md§4). -
First-responder-wins. An approval may be resolved on another device between when a widget/Siri surface renders and when the intent fires (
SYNC §5.4). Intents must handle "already resolved" as a normal, non-error outcome (return a friendlyIntentResultdialog, not a throw).
4. Opportunity catalog — iPhone (NucleicRemote) — highest value
The phone is where App Intents pays off most: it is the "act from anywhere" surface, and its extensions (widgets, Live Activity, notifications) already exist to host intent buttons.
4.1 ⭐ Approve / Deny an agent from anywhere (the flagship intent)
- What:
AnswerApprovalIntent(approval:, decision:)→ wrapsRemoteStore.respond(approval:decision:)(RemoteStore.swift~818–827) /respondFromNotification(~124–136). - Where it shows up:
- Interactive Live Activity / Dynamic Island button. Today the Live Activity is display-only
with a
Linktonucleic://session/<id>(SessionLiveActivity.swift~135). Replace the link withButton(intent: AnswerApprovalIntent(...))for low/medium-risk approvals → allow/deny without unlocking into the app. This is the highest-leverage single change in this document. - Home/Lock-screen widget (
NucleicRemoteWidgets/): a "Top approval" widget whose Allow/Deny areButton(intent:). - Siri / Shortcuts: "Hey Siri, approve the agent" (disambiguates when >1 pending via the
EntityQueryin §4.6). - Apple Watch / Control Center later — same intent, new surface.
- Interactive Live Activity / Dynamic Island button. Today the Live Activity is display-only
with a
- Parameters: an
ApprovalEntity(id =ApprovalID) + aDecision-backedAppEnum(.allow,.deny, and the threeAllowAlwaysScopecases fromApproval.swift:12-19, excluded for destructive). - Constraints: §3.1 (live channel), §3.3 (risk gating — high-risk falls back to
openAppWhenRun+ biometric), §3.4 (already-resolved is a friendly no-op). - Backing model:
ApprovalRequest(Protocol/Approval.swift:21-52) — alreadyCodable.
4.2 ⭐ Unblock a session with a follow-up prompt
- What:
SendFollowUpIntent(session:, text:)→RemoteStore.sendInput(text:sessionID:)(RemoteStore.swift~829–833). This is the "type the next step to unblock a paused agent" flow thatUX_IOS.md§2 explicitly keeps atapprovescope. - Where: Siri ("tell the auth-refactor agent to run the tests"), a widget "Reply" affordance,
Shortcuts automation. The
textparameter supports Siri dictation naturally. - Value: turns a blocked
awaitingInputsession green from a voice command or a Shortcut.
4.3 "What needs me?" — a query intent + Spotlight
- What:
SessionsNeedingMeIntentreturning[SessionEntity]filtered by the existing attention rule (needsAttention: awaitingApproval, or awaitingInput && not completed — see the macOSSessionSummary.needsAttentionatSessionDetailView.swift~255–265; the iOS store carries the samependingApprovalCount/statusonWireSessionSummary,RemoteStore.swift:46). - Where: Siri ("what needs me in Nucleic?"), Spotlight (index sessions so a search surfaces the waiting one), Shortcuts (feed the list into a notification/automation).
- Backing:
SessionStatus(CoreIdentifiers.swift:182-229) +TurnDisposition(~234–239).
4.4 Open a specific session (deep-link intent)
- What:
OpenSessionIntent(session:)withopenAppWhenRun→ reuse the existingnucleic://session/<id>route (RemoteStore.handleDeepLink~112–118). Nearly free given the deep-link infra already exists. - Where: Spotlight result tap, Siri ("open payment-flow"), a widget row, Shortcuts.
4.5 Answer an "Ask User Question"
- What:
AnswerQuestionIntentmapping to theAskUserQuestionCardViewflow (AskUserQuestionCardView.swift~48–210). Because these are structured single/multi-select + free-text, they map cleanly onto App Intents parameter disambiguation and could even be voiced. Medium priority — richer UX than a plain approval, but the in-app card already handles it well.
4.6 Supporting entities & queries (shared infra for the above)
SessionEntity(idSessionID) withEntityQuery+ aneedsAttentionEntityQueryvariant.ApprovalEntity(idApprovalID) with a "pending approvals"EntityQuery.ProjectEntity(idProjectID) — enables "start a session in ProjectX" (control scope, §5).- All three ids are already
Codable/Hashable/Identifiable(CoreIdentifiers.swift).
4.7 Control-scope intents (ship after scope=control lands)
UX_IOS.md §2 defers these; wire the intents but gate on RemoteStore.canControl:
StartSessionIntent(project:, prompt:, model?, effort?, useWorktree, auto?)→RemoteStore.startChat(...)(~839–846). Great "Hey Siri, start a Nucleic agent to …" entry.IntegrateSessionIntent(session:, strategy:)→RemoteStore.integrate(id:, mode:)(~870), with aIntegrationStrategyAppEnum(merge/rebase/squash).InterruptSessionIntent,DiscardSessionIntent,ArchiveSessionIntent→interrupt/discard/setArchived(~865–873).
4.8 To-do capture & dispatch (a natural Siri/Shortcuts win)
CaptureTodoIntent(text:, project?)→RemoteStore.captureTodo(...)(~849–852). "Hey Siri, add a Nucleic to-do: fix the flaky payment test" — capture on the go, dispatch to an agent later.DispatchTodoIntent(todo:)→RemoteStore.dispatchTodo(...)(~855).TodoID/TodoStatusalready exist (CoreIdentifiers.swift:99-119).
5. Opportunity catalog — macOS host (NucleicApp)
The Mac already has the richest UI, so App Intents here is about automation and voice, not filling a gap. Gate all of this behind the §3.2 packaging spike (SwiftPM executable → App Intents metadata extraction) — that's the prerequisite.
5.1 Start a session by voice / Shortcut (highest macOS value)
StartSessionIntent(project:, prompt:, model?, effort?, base?, useWorktree, auto?, autoShip?)→AppStore.startChat()(AppStore.swift:2145) /SessionController.start(prompt:)(~614). Enables "Start a Claude agent in to " from Siri/Shortcuts/Action button, and lets power users script fleet launches. All parameters map toNewChatComposerfields (~509–560).
5.2 Approve from the menu bar / Siri without foregrounding
AnswerApprovalIntent→AppStore.respondToApproval(id:decision:by:)(AppStore.swift:7295). The Mac already has a menu-bar "needs you" surface (UX_MACOS.md§7); an intent lets Siri/Shortcuts answer low-risk approvals hands-free (respecting the §3.3 risk gate and theby responderlabel so multi-device attribution stays correct).
5.3 Integrate / ship a finished session
IntegrateSessionIntent(session:, strategy:)→AppStore.integrateOpenSession(strategy)(AppStore.swift:4700) /SessionController.integrate(...)(~1226).IntegrationStrategy(WorktreeManager.swift:90-93) becomes anAppEnum. Note: interactive conflict handling (UX_MACOS.md§6) means the intent should return status and route to the app on conflict rather than resolve it headlessly.
5.4 Query intents & Spotlight for triage
DashboardStatsIntent→AppStore.dashboardStats(AppStore.swift:5-28: projects, chats, activeChats, messages, tokens…). "How many agents are running?" / a Shortcut that posts a daily summary.SessionsNeedingMeIntent(macOS twin of §4.3) overAppStore.summaries.- Spotlight indexing of sessions/projects via
CoreSpotlight(not present today — agent confirmed noCSSearchableIndexusage) so a system search jumps to a session; pairs with anOpenSessionIntent.
5.5 Session control & config as Shortcut actions
Thin wrappers, useful for automations (e.g. "at 6pm, interrupt all running agents"):
InterruptSessionIntent, ArchiveSessionIntent, SetModelIntent, SetEffortIntent,
SetAutoIntent, SetAutoShipIntent → the corresponding SessionController setters
(setModel ~989, setEffort ~1003, setAuto ~1029, setAutoShip ~1048, setArchived ~1134,
interrupt ~974). Lower priority; batch behind AppShortcuts once the core intents ship.
6. Proposed App Intents surface (types)
A minimal, shared vocabulary that most intents above draw from:
Entities
SessionEntity—id: SessionID, display title/project/status;EntityQuery+ "needs me" query.ApprovalEntity—id: ApprovalID, title, risk; query over pending approvals.ProjectEntity—id: ProjectID, name, default branch.TodoEntity—id: TodoID, text, status (iOS-first).
Enums (AppEnum)
ApprovalDecisionOption— fromDecision+AllowAlwaysScope(Approval.swift:12-61); destructive-safe subset.RiskLevel— fromRisk(Approval.swift:5-10) — drives inline-vs-open gating.IntegrationStrategyOption— fromIntegrationStrategy(WorktreeManager.swift:90-93).BackendOption— fromBackendID(CoreIdentifiers.swift:48-83).EffortOption— low/medium/high/xhigh/max/orchestra (setEffortdomain).
Intents (grouped by scope/priority)
- approve-scope, iOS-first:
AnswerApprovalIntent,SendFollowUpIntent,AnswerQuestionIntent,SessionsNeedingMeIntent,OpenSessionIntent,CaptureTodoIntent,DispatchTodoIntent. - control-scope / macOS:
StartSessionIntent,IntegrateSessionIntent,InterruptSessionIntent,DiscardSessionIntent,ArchiveSessionIntent, config setters,DashboardStatsIntent. NucleicShortcuts: AppShortcutsProvider— curates the few voice-worthy ones (approve, unblock, start, "what needs me").
All backing types are already Sendable/Codable (both agents confirmed), so AppEntity/AppEnum
conformances are additive — no model refactor required.
7. Suggested phasing
- Phase 0 — de-risk (½–1 day each):
- iOS: prototype
AnswerApprovalIntentas aButton(intent:)inside the existing Live Activity (SessionLiveActivity.swift) for a low-risk approval. Proves the extension→host channel path under §3.1. - macOS: prove App Intents metadata extraction works for a SwiftPM
.executablewrapped as an.app(§3.2). Do not scope macOS Shortcuts before this passes.
- iOS: prototype
- Phase 1 — iOS approve-scope (highest ROI):
AnswerApprovalIntent,SendFollowUpIntent,SessionsNeedingMeIntent,OpenSessionIntent, entities/enums,AppShortcuts. Wire into the interactive Live Activity + a new interactive widget. Reuse the notification approve/deny contract for the connectivity/queueing semantics. - Phase 2 — iOS breadth:
AnswerQuestionIntent, to-do intents, Control Center control, Spotlight indexing of sessions. - Phase 3 — macOS (post-spike):
StartSessionIntent,IntegrateSessionIntent, query intents, Spotlight, config-setter Shortcut actions. - Phase 4 — control-scope on iOS: unlock start/integrate/discard/interrupt once
scope=controlships (UX_IOS.md§2, open-Q). Intents already written in Phase 3 mostly port over.
8. Risks & open questions
- macOS metadata extraction under SwiftPM (§3.2) — the gating unknown; spike first.
- Extension-process connectivity (§3.1) — a widget/Siri intent may run when the host is
unreachable. Decide per-intent: fail-clean-with-dialog vs.
openAppWhenRunfallback vs. the existing ~30 s queue-on-reconnect used byrespondFromNotification. - Risk gating parity (§3.3) — App Intents surfaces must reproduce the "no inline Allow on destructive/network/hostExec, biometric on open" rule exactly, or they'd become a softer approval path than the UI — a security regression. Encode it once in a shared helper both the UI and the intents call.
- First-responder-wins (§3.4) — treat "already resolved elsewhere" as success, never an error.
- Scope drift — keep every intent behind the same scope check the UI enforces
(
canControl/ grantedDeviceScope); never let an intent widen effective scope. - Attribution — thread a stable
responderlabel (device name + identity fingerprint, as the UI does atAppStore.respondToApproval(... by:)) through intent-driven approvals so the audit trail stays truthful. - Discoverability vs. noise — curate
AppShortcutsto the ~4 genuinely voice-worthy actions (approve, unblock, start, "what needs me"); expose the long tail through Shortcuts only.
Prepared from a code-level survey of Sources/NucleicApp, Sources/NucleicCore,
Sources/NucleicProtocol, and ios/NucleicRemote. No App Intents code exists yet; all
file:line anchors point at the actions and models an implementation would wrap.