Skip to content

feat(mobile): add four app actions for OS surfaces - #6200

Merged
iscekic merged 1 commit into
mainfrom
kwf/app-intents-action-contract-c700
Sep 17, 2026
Merged

iscekic merged 1 commit into
mainfrom
kwf/app-intents-action-contract-c700

Conversation

@iscekic

@iscekic iscekic commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Changelog for users

  • Four actions are now available from outside the app: start an agent, open the waiting agent, open a session, and open a pull request.
  • On iOS they appear in the Shortcuts app; on Android the same four are addressable from the launcher, the Assistant, or another app.
  • Start agent runs with the app closed, starts the session through the in-app create path, and reports the new session to the caller.
  • Start agent with an unsupported GitHub or GitLab repository reports a repository-specific reason and a next action.
  • A blank start-agent request now answers with the prompt-required refusal instead of letting the caller wait out the timeout.
  • Open agent needing input opens the only waiting session, or the agents list when none or several wait.
  • Open session and open pull request open the same screens their in-app controls reach; open pull request with a link that is not a review link reports the app's not-a-pull-request message and opens nothing.
  • The four action titles, parameter labels and shortcut titles keep the same wording, and their translations are unchanged.

Changelog for maintainers

  • apps/mobile/src/lib/app-actions/app-action-dispatch.ts:207 — accepted. The handler threw whenever the parser rejected a payload, so a blank StartAgent never reached the action path and Android's fire got no completeAppAction, timing out silently after 15 s (KiloActionActivity.kt:149-166). The parser now keeps a StartAgent with no usable prompt or session as that action (app-action-contract.ts:423-433), so start-agent.ts:55-58 returns the contract's non-retryable empty-prompt result for the entry point to report.
  • apps/mobile/modules/kilo-app-actions/android/src/main/AndroidManifest.xml:19 — rejected, no change. The exported entry point is the required design, not an oversight: the same four actions must be addressable from the launcher, the Assistant or another app (AndroidManifest.xml:3-15,21), and a permission or caller check would reject exactly those callers, which cannot hold a signature permission. The activity holds no action logic — it translates the intent and hands it to the shared dispatcher (KiloActionActivity.kt:11-18,141-167) — and StartAgent refuses when no user is signed in (start-agent.ts:71-74).
  • apps/mobile/modules/kilo-app-actions/android/src/main/res/xml/actions.xml:14 — accepted. The file was referenced by no meta-data and only repeated the four capability declarations, so it had no effect. Deleted it; the activity's android.app.shortcuts meta-data points at the one capability file, res/xml/shortcuts.xml (AndroidManifest.xml:60), and the adjacent comment now names that file. Tests assert res/xml/ holds only shortcuts.xml and that it declares one capability per action.
  • apps/mobile/modules/kilo-app-actions/ios/KiloAppActionBridge.swift:68 — accepted. The singleton kept the JS dispatcher and runtime with no teardown, so an App Intent could still execute on a dead runtime after the module was destroyed. Added unregister() clearing the dispatcher, runtime and parked buffer (KiloAppActionBridge.swift:82-88), called from the module's OnDestroy (KiloAppActionsModule.swift:31-33), mirroring Android's AppActionDispatcher.clear() (KiloAppActionsModule.kt:51-53).
  • One pure contract module describes the four actions — ids, URL grammar, payload fields, destinations and result shapes — and both platforms translate into it; destinations resolve through the same helpers the in-app controls use. On iOS, four App Intents and an AppShortcutsProvider list all four, Start agent runs in the background and the three open actions bring the app forward. On Android, one exported entry point declares one intent-filter per action and points its shortcuts meta-data at the single capability file; Assistant and launcher surfacing additionally needs Play Console capability configuration the app cannot provide, so the exported entry points are the part the app ships — built but not proved on a device.
  • Start agent resolves the repository, the stored model preference and the signed-in safe-retry outbox headlessly, then runs the same prepare core the in-app create controls now use; the in-app new-session and Continue hooks were refactored onto that shared core with their navigation, feedback, haptics and analytics unchanged. Open actions park a destination in a single pending slot that the tabs layout consumes — the only place holding the router and the live session list — and a review link the provider resolvers cannot parse reports a refusal instead of opening nothing. App Intent copy is localized by a prebuild plugin that writes the app target's Localizable.strings; native sources, translations and four-action parity are checked by tests. Riskiest pieces: the boot-time dispatcher registration handshake, and the Android result handoff because Expo's Android API has no awaitable JS promise.
  • The ios job failed because appintentsmetadataprocessor reads the App Intent sources at build time and halts the target with 'LocalizedStringResource' must be initialized with a call to its initializer or a string literal; the shared KiloIntentCopy table exposed the copy as static let values, so every use site referenced a constant. Each intent title, every @Parameter(title:), every shortcut shortTitle and the start-failure message are now string literals at their use sites, and enum KiloIntentCopy is deleted; the inlined values are identical to the removed table, so the translation plugin still finds the same keys and plugins/app-intent-copy.json is unchanged.
  • The contract test scans the Swift source for label lines, requires a quoted literal on each, checks every literal against the plugin's English copy, and pins the count to four titles, four shortcut titles, the failure copy and five parameter labels. Review hint: the extractor parses source text, so moving these strings back behind a shared constant or a computed expression will fail the build again — keep the literals at the use sites. The kiloapp:///actions/<slug> targets, the boot-time dispatcher handshake and the Android entry points are untouched.

E2E proof

— log excerpts

[e1] # kwf-fix: proof-9fc177e — prove PR 6200 live on this host's platform (andr -> pass :: android only (host has no iOS): blank StartAgent answered in 1789 ms with no session (e1rv2-warm.log) instead of the 15 s timeout, running-app StartAgent created ses_f552a1a8bffeOBx8XjbQlFb9eB and the app opened it (e1r-warm3.log), cold StartAgent created ses_f552a9839fffK0SgYiLkZ82yQQ (e1r-cold-session-screen.xml), OPEN_SESSION opened that chat, OPEN_PULL_REQUEST opened #6200 while a non-review link answered in 1704 ms and opened nothing (e1r-badlink-needstwo.log), OPEN_NEEDS_INPUT opened the single waiting chat and the agents list with none waiting; GitHub-dependent parts ran against the started github stub because the harness token seed fails on this host, and the iOS Shortcuts-app list c

@iscekic
iscekic marked this pull request as draft September 16, 2026 06:07
Comment thread apps/mobile/src/lib/app-actions/app-action-dispatch.ts
Comment thread apps/mobile/modules/kilo-app-actions/android/src/main/res/xml/actions.xml Outdated
Comment thread apps/mobile/modules/kilo-app-actions/ios/KiloAppActionBridge.swift
@kilo-code-bot

kilo-code-bot Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental re-review of the 16 files changed since 40acdaa — the app-action empty-prompt fix, the iOS dispatcher teardown, the Xcode Resources-phase merge, the inlined App Intent literals, and their tests — found no new correctness, security, performance, or memory-leak defects. All three accepted prior findings are fixed, and the fourth (the exported Android entry point) was re-verified as a deliberate, requirement-driven design choice rather than a defect.

Files Reviewed (16 files)
  • apps/mobile/src/lib/app-actions/app-action-contract.ts — a blank StartAgent now reaches the action path; whitespace prompts still classify as the non-retryable empty-prompt (start-agent.ts:56-58), and a usable sessionId is never displaced.
  • apps/mobile/src/lib/app-actions/app-action-contract.test.ts, app-action-dispatch.test.ts, start-agent.test.ts — assertions match the new parser behavior.
  • apps/mobile/src/lib/app-actions/start-agent-runtime.tsreadStoredValue is a behavior-preserving pass-through to SecureStore.getItemAsync for the same key, wrapped by the existing try/catch.
  • apps/mobile/plugins/app-intent-resources.js (new), apps/mobile/plugins/withAppIntentLocalizations.js — the merge folds the created Resources phase into the target's existing one and drops the empty duplicate; matches existing addBuildPhase / pbxNativeTargetSection() usage.
  • apps/mobile/modules/kilo-app-actions/ios/KiloAppActionBridge.swift, ios/KiloAppActionsModule.swiftunregister() is lock-guarded, the wait stays bounded by waitUntilRegistered, and the previously retained dispatcher/runtime is released; OnDestroy is a valid Expo Module hook (same shape as kilo-surface-geometry).
  • apps/mobile/modules/kilo-app-actions/ios/KiloAppIntents.swift — every inlined literal matches the copy table; no non-literal LocalizedStringResource construct remains.
  • apps/mobile/modules/kilo-app-actions/platform-parity.test.ts, ios-app-intents.test.ts, android-app-actions.test.ts — no references to the deleted actions.xml; the manifest comment and tests now describe the single capability file.
  • apps/mobile/modules/kilo-app-actions/android/src/main/AndroidManifest.xml
  • apps/mobile/modules/kilo-app-actions/android/src/main/res/xml/actions.xml (deleted)
  • apps/mobile/src/lib/app-actions/platform-parity.test.ts (new)
Previous Review Summary (commit 40acdaa)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 40acdaa)

Status: 4 Issues Found | Recommendation: Address before merge

Fix these issues in Kilo Cloud

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 2
Issue Details (click to expand)

WARNING

File Line Issue
apps/mobile/src/lib/app-actions/app-action-dispatch.ts 207 A blank/whitespace StartAgent prompt is rejected as an unrecognized payload, so the contract's non-retryable empty-prompt result never reaches the OS caller; Android skips completeAppAction and relies on its 15 s timeout with no message.
apps/mobile/modules/kilo-app-actions/android/src/main/AndroidManifest.xml 19 The exported KiloActionActivity has no permission or caller check, so any installed app can invoke START_AGENT and create sessions on the signed-in account.

SUGGESTION

File Line Issue
apps/mobile/modules/kilo-app-actions/android/src/main/res/xml/actions.xml 14 actions.xml is referenced by no manifest meta-data, so its capability declarations have no effect and duplicate shortcuts.xml.
apps/mobile/modules/kilo-app-actions/ios/KiloAppActionBridge.swift 68 The singleton retains the JS dispatcher and runtime with no teardown path, unlike the Android module's OnDestroy clear.
Files Reviewed (128 files)
  • apps/mobile/app.config.ts
  • apps/mobile/index.js
  • apps/mobile/vitest.pure.config.ts
  • apps/mobile/plugins/app-intent-copy.js, app-intent-copy.json, withAppIntentLocalizations.js
  • apps/mobile/modules/kilo-app-actions/expo-module.config.json
  • apps/mobile/modules/kilo-app-actions/android/build.gradle, android/src/main/AndroidManifest.xml, android/src/main/res/xml/actions.xml, android/src/main/res/xml/shortcuts.xml
  • apps/mobile/modules/kilo-app-actions/android/src/main/java/expo/modules/kiloappactions/KiloActionActivity.kt, KiloAppActionsModule.kt
  • apps/mobile/modules/kilo-app-actions/ios/KiloAppActions.podspec, KiloAppActionsModule.swift, KiloAppActionBridge.swift, KiloAppIntents.swift
  • apps/mobile/modules/kilo-app-actions/android-app-actions.test.ts, ios-app-intents.test.ts, platform-parity.test.ts, app-intent-copy.test.ts
  • apps/mobile/src/app/(app)/(tabs)/_layout.tsx, apps/mobile/src/app/+native-intent.tsx
  • apps/mobile/src/components/agents/use-continue-cloud-create.ts, use-new-session-creator.ts
  • apps/mobile/src/lib/app-actions/app-action-contract.ts, app-action-dispatch.ts, action-url-handler.ts, native-bridge.ts, pending-app-action.ts, prepare-agent-session.ts, start-agent.ts, start-agent-runtime.ts, use-pending-app-action.ts
  • apps/mobile/src/lib/app-actions/*.test.ts, use-pending-app-action.mounted.test.tsx
  • apps/mobile/src/i18n/locales/*.json (87 catalogs; new appActions.start.* keys carry the matching {{repository}} placeholders)

Reviewed the shared prepareAgentSession refactor for fingerprint/operation-key parity against the previous in-app creators (fingerprint key order, legacy GitHub row migration, autoCommit/variant/attachmens handling, and success/terminal rotate+remove ordering are preserved), the boot-time dispatcher registration and buffered replay ordering, the pending-slot subscribe/take lifecycle, and the iOS/Android native hand-shake for retained listeners or unbounded state. No memory leak was found: the listener sets unsubscribe symmetrically, the Android waiter/buffer maps are bounded and cleared, and the module-level intentKeys map is cleared by rotateKey.


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

iscekic added a commit that referenced this pull request Sep 16, 2026
@iscekic
iscekic force-pushed the kwf/app-intents-action-contract-c700 branch from 40acdaa to 65dd4c8 Compare September 16, 2026 06:49
@iscekic

iscekic commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Cross-platform fix. Pushed 0cf9fb5 (kwf kwf-fix-platform-5dec).

What changed:

  • apps/mobile/src/lib/app-actions/start-agent-runtime.ts:13 — accepted. The action runtime imported expo-secure-store itself, a second storage read beside the app's shared one. It now calls readStoredValue from auth/secure-store-read. That helper's comment records expo-secure-store as available on iOS and Android, so the shared action path keeps one read for both.
  • A new parity check fails if any shared action module branches on the platform or imports a platform-suffixed module. It also requires the action runtime to use that shared read instead of expo-secure-store.
  • The start-agent behavior test now asserts the stored model-preference key goes through the shared read.
  • Review hint: the parity check reads one directory's source text, so a platform branch behind a module outside src/lib/app-actions would not be caught.

@iscekic
iscekic force-pushed the kwf/app-intents-action-contract-c700 branch from 0cf9fb5 to 9fc177e Compare September 16, 2026 12:04
@iscekic
iscekic marked this pull request as ready for review September 16, 2026 12:14
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 16, 2026
@iscekic iscekic self-assigned this Sep 16, 2026
@iscekic

iscekic commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

(bot) kwf reopened its section for this PR: verification restarted (phase verify) The proof in the body came from an earlier round and can be stale. This PR is human-ready, so nothing here was changed.

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

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants