Skip to content

feat(mobile): approve the front waiting agent from the Apple Watch and Wear OS - #6199

Merged
iscekic merged 1 commit into
mainfrom
kwf/approve-from-the-watch-cfaa
Sep 16, 2026
Merged

iscekic merged 1 commit into
mainfrom
kwf/approve-from-the-watch-cfaa

Conversation

@iscekic

@iscekic iscekic commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Changelog for users

  • A waiting agent can be approved from the wrist: the Active Agents Live Activity card on the Apple Watch Smart Stack shows the waiting count and an Approve control.
  • On Android and Wear OS, the ongoing "Active agents" notification shows an Approve action while a permission waits.
  • Approve appears only while a permission request waits, and disappears once that request is answered. A question or retry keeps the count but gets no Approve.
  • Approving from the wrist answers the same permission the phone's Allow Once answers, and the surface count updates after the approval.
  • The Approve label reuses the existing translated "Approve" copy.
  • If the approval cannot be sent or torn down, the surface keeps its count and its Approve control, so tapping again retries; the wrist has no error screen. A refused background start on Android no longer holds the device awake.

Changelog for maintainers

  • apps/mobile/src/lib/glanceable/approve-front-agent.ts:291 — accepted: a rejecting manager factory no longer strands the retained UserWebConnection. The factory destroys the connection before rethrowing, and runApproval catches the rejection, republishes the surfaces from fresh rows, and returns failed, retryable.
  • apps/mobile/modules/active-agents-live-update/android/src/main/java/com/kilocode/activeagentsliveupdate/ActiveAgentsApproveReceiver.kt:20 — accepted: startService sits in a try/catch that logs and returns, and the wake lock is taken only after the start is accepted, so the API 26+ background refusal cannot strand it. A new test reads the Kotlin source and pins that order.
  • apps/mobile/src/lib/glanceable/approve-front-agent.ts:301 — accepted: teardown deletes the headlessConnections entry and destroys the connection before manager.destroy(), and the approval's finally swallows a throwing teardown, so the connection, outcome, and surface refresh all survive.
  • apps/mobile/src/glanceable-ios/approve-action.ts:95 — accepted: a registration id guards the unsubscribe, so an earlier registration that a later one replaced returns without clearing the shared callback or removing the listener.
  • needsApproval is a new optional count on the glanceable snapshot and push schemas: it counts permission rows only, and every reader treats an absent value as 0, so an older server still parses. The Live Activity content state forwards the count; the Home Screen widget and complication props stay read-only and carry no Approve affordance.
  • iOS layout: the Approve Button (literal target="approve") rides the count block, gated on hasCounts && needsApproval > 0, so the expired frame draws no control. A new bannerSmall section serves the .small family (Apple Watch, CarPlay) with one count row plus the control, and the combined accessibility label sits on the count block alone so VoiceOver can focus the button. approve-action.ts matches the interaction event on target alone, because the native source is the ActivityKit instance UUID, and layout-copy.test.ts pins the layout literal to APPROVE_TARGET.
  • Android adds a second notification action whose PendingIntent starts a non-exported receiver and a HeadlessJsTaskService (ActiveAgentsApprove, 60 s); the task registered at app entry applies the stored widget language, then calls the shared approval service. The new approveFrontAgent service picks the longest-waiting permission row, reuses the open chat screen's manager through the new live-session-manager-registry (the relay allows one owner per session) or attaches a headless connection, waits up to 15 s for the replayed ask, responds once, acks attention, and republishes the surfaces from fresh rows. notifications.ts now shares its SecureStore scope reads through lib/glanceable/scope.ts; no file under apps/mobile/src/**/kiloclaw/** changed.
  • Review first: the wire compatibility of the optional field, the stringified-literal target contract between layout and handler, the deliberately swallowed teardown in the approval finally, the receiver's start-then-lock order that its test reads from the Kotlin source, and the swallowed approval failure that leaves the wrist with no error state.

E2E proof

The iOS half of this change ran live on a Mac simulator in PR #6232, which stacks on this branch and pins the behaviour with tests. That round installed the CI build on an iOS simulator and drove the scenarios; its verdict is iOS verification passed.

The Apple Watch wrist tap is an owner-manual scope: the simulator cannot deliver it. It is NOT claimed as proved here.

This section's own earlier line read -> pass :: owner-manual: ..., which the harness now refuses to count as a pass (a scenario naming an owner-manual scope is excluded whatever its result field says).

@iscekic
iscekic marked this pull request as draft September 16, 2026 04:24
Comment thread apps/mobile/src/lib/glanceable/approve-front-agent.ts Outdated
Comment thread apps/mobile/src/lib/glanceable/approve-front-agent.ts Outdated
Comment thread apps/mobile/src/glanceable-ios/approve-action.ts
@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

Files Reviewed (13 files)
  • apps/mobile/modules/active-agents-live-update/android/src/main/java/com/kilocode/activeagentsliveupdate/ActiveAgentsApproveReceiver.kt
  • apps/mobile/src/glanceable-android/approve-receiver-wake-lock.test.ts
  • apps/mobile/src/glanceable-ios/approve-action.ts
  • apps/mobile/src/glanceable-ios/approve-action.test.ts
  • apps/mobile/src/lib/auth/secure-store-read.ts
  • apps/mobile/src/lib/auth/secure-store-value.ts
  • apps/mobile/src/lib/auth/secure-store-value.test.ts
  • apps/mobile/src/lib/glanceable/approve-front-agent.ts
  • apps/mobile/src/lib/glanceable/approve-front-agent.test.ts
  • apps/mobile/src/lib/glanceable/scope.ts
  • apps/mobile/src/lib/glanceable/scope.test.ts
  • apps/mobile/src/lib/glanceable/scope-cross-platform.test.ts
  • apps/mobile/src/lib/tool-summary-translation/tool-summary-translation-client.ts
Previous Review Summary (commit e751df3)

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

Previous review (commit e751df3)

Status: 4 Issues Found | Recommendation: Address before merge

Overview

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

WARNING

File Line Issue
apps/mobile/src/lib/glanceable/approve-front-agent.ts 291 A throwing manager factory leaks the retained UserWebConnection (no map entry) and the rejection escapes runApproval, skipping refreshSurfaces.
apps/mobile/modules/active-agents-live-update/android/src/main/java/com/kilocode/activeagentsliveupdate/ActiveAgentsApproveReceiver.kt 20 startService can throw on Android 8+ for a backgrounded app, so the wrist approval silently never runs and the wake lock taken on line 19 is never released.

SUGGESTION

File Line Issue
apps/mobile/src/lib/glanceable/approve-front-agent.ts 301 If manager.destroy() throws, the module-level headlessConnections entry and its connection leak and refreshSurfaces is skipped.
apps/mobile/src/glanceable-ios/approve-action.ts 95 The unsubscribe clears module-level state instead of this registration's, so a second registration plus the first unsubscribe disables Approve (latent; not reachable today).
Files Reviewed (42 files)
  • apps/mobile/index.js
  • apps/mobile/modules/active-agents-live-update/android/build.gradle
  • apps/mobile/modules/active-agents-live-update/android/src/main/AndroidManifest.xml
  • apps/mobile/modules/active-agents-live-update/android/src/main/java/com/kilocode/activeagentsliveupdate/ActiveAgentsApproveReceiver.kt - 1 issue
  • apps/mobile/modules/active-agents-live-update/android/src/main/java/com/kilocode/activeagentsliveupdate/ActiveAgentsApproveTaskService.kt
  • apps/mobile/modules/active-agents-live-update/android/src/main/java/com/kilocode/activeagentsliveupdate/ActiveAgentsLiveUpdateModule.kt
  • apps/mobile/src/components/agents/live-session-manager-registry.test.ts
  • apps/mobile/src/components/agents/live-session-manager-registry.ts
  • apps/mobile/src/components/agents/session-provider.test.tsx
  • apps/mobile/src/components/agents/session-provider.tsx
  • apps/mobile/src/glanceable-android/android-sink.test.ts
  • apps/mobile/src/glanceable-android/android-sink.ts
  • apps/mobile/src/glanceable-android/approve-task.test.ts
  • apps/mobile/src/glanceable-android/approve-task.ts
  • apps/mobile/src/glanceable-android/live-update.ts
  • apps/mobile/src/glanceable-android/register.ts
  • apps/mobile/src/glanceable-android/widget-props.test.ts
  • apps/mobile/src/glanceable-android/widget-props.ts
  • apps/mobile/src/glanceable-ios/active-agents-live-activity-a11y.test.ts
  • apps/mobile/src/glanceable-ios/active-agents-live-activity.tsx
  • apps/mobile/src/glanceable-ios/approve-action.test.ts
  • apps/mobile/src/glanceable-ios/approve-action.ts - 1 issue
  • apps/mobile/src/glanceable-ios/layout-copy.test.ts
  • apps/mobile/src/glanceable-ios/layout-copy.ts
  • apps/mobile/src/glanceable-ios/register.test.ts
  • apps/mobile/src/glanceable-ios/register.ts
  • apps/mobile/src/glanceable-ios/view-props.test.ts
  • apps/mobile/src/glanceable-ios/view-props.ts
  • apps/mobile/src/lib/glanceable/approve-front-agent.test.ts
  • apps/mobile/src/lib/glanceable/approve-front-agent.ts - 2 issues
  • apps/mobile/src/lib/glanceable/front-approval.test.ts
  • apps/mobile/src/lib/glanceable/front-approval.ts
  • apps/mobile/src/lib/glanceable/scope.test.ts
  • apps/mobile/src/lib/glanceable/scope.ts
  • apps/mobile/src/lib/notifications.test.ts
  • apps/mobile/src/lib/notifications.ts
  • packages/app-shared/src/glanceable-agents-snapshot.test.ts
  • packages/app-shared/src/glanceable-agents-snapshot.ts
  • packages/notifications/src/push-data.test.ts
  • packages/notifications/src/push-data.ts
  • services/notifications/src/lib/glanceable-delivery.test.ts
  • services/notifications/src/lib/glanceable-delivery.ts

Fix these issues in Kilo Cloud


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

Review guidance: REVIEW.md from base branch main

@iscekic
iscekic force-pushed the kwf/approve-from-the-watch-cfaa branch from e751df3 to 47e5680 Compare September 16, 2026 06:00
@iscekic

iscekic commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Cross-platform fix. Pushed 86c00ef (kwf kwf-fix-platform-0b49).

What changed:

  • apps/mobile/src/lib/auth/secure-store-read.ts — accepted: the added expo-secure-store import is gone; the retrying credential read now calls the shared cross-platform helper.
  • apps/mobile/src/lib/glanceable/scope.ts — accepted: the added expo-secure-store import is gone; both scope reads now call the same shared helper.
  • One shared helper owns the only platform import: readStoredValue forwards the key and its options, and the credential, scope and translation clients all call it.
  • The helper's comment records why no per-platform branch is kept: expo-secure-store exists on iOS and Android alike, so no platform lacks the capability.
  • A rejected read still propagates to the caller; the scope reads catch it and return null, so both callers see the same values as before.
  • Tests pin the single entry point: one mocks the platform module for the key, the forwarded options and a propagated rejection; the scope test reads its module from disk and forbids a direct platform import.
  • Review first: the behaviour equivalence of the moved read, the single-owner boundary, and the source-reading test's dependency on a module path literal.

@iscekic
iscekic force-pushed the kwf/approve-from-the-watch-cfaa branch from 86c00ef to c40943e Compare September 16, 2026 08:06
@iscekic
iscekic marked this pull request as ready for review September 16, 2026 08:16
@iscekic

iscekic commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

(bot) ## Live proof (proof-only request; no code change)

Ran on android; the request asked for any platform.

This request asked for live proof of existing behaviour. There is no diff; the verify round ran the must-run scenarios and recorded the evidence below.

[e1] Prove PR 6199 live: Approve a waiting agent from the ongoing surface -> pass :: owner-manual: the Apple Watch Live Activity card count, the wrist Approve tap, the complication count update and the Wear OS watch surfaces (no watch host, IOS BOOTED none); on the owned android emulator-5554 the bought behaviour is proved live 2026-09-16 — a real ask stayed permission for 30 s untouched (e1r-db-control-30s.log, e1r-cli-control-30s.txt), the app-posted notification's Approve tap moved the row permission->idle with '1 Idle' actions=1 and the CLI wrote /tmp/vf-approve-1.txt (e1r-db-after-appium-tap.log, e1r-notification-after.log, e1r-cli-after.txt, e1r-probe-file.log), a question kept the count with no Approve (e1r-notification-question.log), an unanswerable approval kept the
/home/igor_kilocode_ai/.local/share/kwf/sections/kwf-fix-proof-c40943e-5c71/e2e-mobile-app/e1r-notification-before.log
      numWithBigPicture=0
      numForegroundService=0
      numUserInitiatedJob=0
      numOngoing=0
      numAutoCancel=0
      numWithLargeIcon=0
      numWithInbox=0
      numWithMediaSession=0
      numWithTitle=0
      numWithText=0
      numWithSubText=0
      numWithInfoText=0
      numRateViolations=0
      numAlertViolations=0
      numQuotaViolations=0
      numImagesRemoved=0
      numTooOld=0
      note_imp_noisy_: [0, 0, 0, 0, 0, 0]
      note_imp_quiet_: [0, 0, 0, 0, 0, 0]
      note_importance_: [0, 0, 0, 0, 0, 0]
      numUndecorateRVs=0
    }
    mStatsArrays.size(): 1
    mStats.size(): 4
/home/igor_kilocode_ai/.local/share/kwf/sections/kwf-fix-proof-c40943e-5c71/e2e-mobile-app/e1r-db-control-30s.log
ses_f55bd5981ffeUufS6zlDEUAAB7|permission|Create /tmp/vf-approve-1.txt
/home/igor_kilocode_ai/.local/share/kwf/sections/kwf-fix-proof-c40943e-5c71/e2e-mobile-app/e1r-cli-control-30s.txt
                                                                                                                                                                                    Reasoning           
                                                                                                                                                                                    Cache read          
                                                                                                                                                                                    Cache write         
                                                                                                                                                                                    Cache rate          
                                                                                                                                                                                    Cost                
                                                                                                                                                                                    ▼ Models (0)
                                                                                                                                                                                    No model usage yet
                                                                                                                                                                                    Code Indexing
                                                                                                                                                                                    • Disabled
/home/igor_kilocode_ai/.local/share/kwf/sections/kwf-fix-proof-c40943e-5c71/e2e-mobile-app/e1r-db-after-appium-tap.log
ses_f55bd5981ffeUufS6zlDEUAAB7|idle|Create /tmp/vf-approve-1.txt
/home/igor_kilocode_ai/.local/share/kwf/sections/kwf-fix-proof-c40943e-5c71/e2e-mobile-app/e1r-notification-after.log
      numWithBigPicture=0
      numForegroundService=0
      numUserInitiatedJob=0
      numOngoing=0
      numAutoCancel=0
      numWithLargeIcon=0
      numWithInbox=0
      numWithMediaSession=0
      numWithTitle=0
      numWithText=0
      numWithSubText=0
      numWithInfoText=0
      numRateViolations=0
      numAlertViolations=0
      numQuotaViolations=0
      numImagesRemoved=0
      numTooOld=0
      note_imp_noisy_: [0, 0, 0, 0, 0, 0]
      note_imp_quiet_: [0, 0, 0, 0, 0, 0]
      note_importance_: [0, 0, 0, 0, 0, 0]
      numUndecorateRVs=0
    }
    mStatsArrays.size(): 1
    mStats.size(): 4
/home/igor_kilocode_ai/.local/share/kwf/sections/kwf-fix-proof-c40943e-5c71/e2e-mobile-app/e1r-cli-after.txt
                                                                                                                                                                                    Input               
     Created /tmp/vf-approve-1.txt with content hello.                                                                                                                              Output              
                                                                                                                                                                                    Reasoning           
     ▣  Code · DeepSeek: DeepSeek V4 Flash 0731 · 1m 54s                                                                                                                            Cache read          
                                                                                                                                                                                    Cache write         
                                                                                                                                                                                    Cache rate          
                                                                                                                                                                                    Generation speed    
                                                                                                                                                                                    Cost                
                                                                                                                                                                                    ▼ Models (1)
/home/igor_kilocode_ai/.local/share/kwf/sections/kwf-fix-proof-c40943e-5c71/e2e-mobile-app/e1r-probe-file.log
-rw-rw-r-- 1 igor_kilocode_ai igor_kilocode_ai 5 Sep 16 12:50 /tmp/vf-approve-1.txt
content:
hello
/home/igor_kilocode_ai/.local/share/kwf/sections/kwf-fix-proof-c40943e-5c71/e2e-mobile-app/e1r-notification-question.log
      numWithBigPicture=0
      numForegroundService=0
      numUserInitiatedJob=0
      numOngoing=0
      numAutoCancel=0
      numWithLargeIcon=0
      numWithInbox=0
      numWithMediaSession=0
      numWithTitle=0
      numWithText=0
      numWithSubText=0
      numWithInfoText=0
      numRateViolations=0
      numAlertViolations=0
      numQuotaViolations=0
      numImagesRemoved=0
      numTooOld=0
      note_imp_noisy_: [0, 0, 0, 0, 0, 0]
      note_imp_quiet_: [0, 0, 0, 0, 0, 0]
      note_importance_: [0, 0, 0, 0, 0, 0]
      numUndecorateRVs=0
    }
    mStatsArrays.size(): 1
    mStats.size(): 4
/home/igor_kilocode_ai/.local/share/kwf/sections/kwf-fix-proof-c40943e-5c71/e2e-mobile-app/e1r-notification-after-retry.log
      numWithBigPicture=0
      numForegroundService=0
      numUserInitiatedJob=0
      numOngoing=0
      numAutoCancel=0
      numWithLargeIcon=0
      numWithInbox=0
      numWithMediaSession=0
      numWithTitle=0
      numWithText=0
      numWithSubText=0
      numWithInfoText=0
      numRateViolations=0
      numAlertViolations=0
      numQuotaViolations=0
      numImagesRemoved=0
      numTooOld=0
      note_imp_noisy_: [0, 0, 0, 0, 0, 0]
      note_imp_quiet_: [0, 0, 0, 0, 0, 0]
      note_importance_: [0, 0, 0, 0, 0, 0]
      numUndecorateRVs=0
    }
    mStatsArrays.size(): 1
    mStats.size(): 4
/home/igor_kilocode_ai/.local/share/kwf/sections/kwf-fix-proof-c40943e-5c71/e2e-mobile-app/e1r-agents-count-scene.log
android.widget.TextView Automatic PR reviews tappable [189,981][933,1018]
android.widget.Button Security Agent, Find and remediate vulnerabilities tappable [55,1074][1025,1217]
android.widget.TextView Security Agent tappable [189,1102][933,1148]
android.widget.TextView Find and remediate vulnerabilities tappable [189,1152][933,1189]
android.widget.TextView REVIEWS tappable [55,1272][1025,1316]
android.widget.Button PR Review, Review pull requests on mobile tappable [55,1344][1025,1487]
android.widget.TextView PR Review tappable [189,1372][933,1418]
android.widget.TextView Review pull requests on mobile tappable [189,1422][933,1459]
android.widget.TextView APP tappable [55,1542][1025,1586]
android.widget.Button Preferences, Appearance, notifications, thinking, and screen behavior tappable [55,1614][1025,1758]
android.widget.TextView Preferences tappable [189,1642][933,1688]
android.widget.TextView Appearance, notifications, thinking, and screen behavior tappable [189,1692][933,1729]
android.widget.Button Tutorial tappable [55,1785][1025,1919]
android.widget.TextView Tutorial tappable [189,1829][933,1875]
android.widget.TextView LINKED ACCOUNTS tappable [55,1974][1025,2018]
android.widget.TextView Email tappable [189,2074][998,2120]
android.widget.TextView e2e-mobile-kwf-fix-proof-c40943e-5c71-android@example.com tappable [189,2124][998,2153]
android.view.View Home, tab, 1 of 3 tappable [0,2195][360,2337]
android.widget.TextView HOME tappable [13,2281][347,2320]
android.view.View Agents, 1 needs input, tab, 2 of 3 tappable [360,2195][720,2337]
android.widget.TextView 1 tappable [539,2200][589,2250]
android.widget.TextView AGENTS tappable [373,2281][707,2320]
android.view.View Profile, tab, 3 of 3 tappable [720,2195][1080,2337]
android.widget.TextView PROFILE tappable [733,2281][1067,2320]
/home/igor_kilocode_ai/.local/share/kwf/sections/kwf-fix-proof-c40943e-5c71/e2e-mobile-app/e1r-db-count-source.log
1|permission

@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 added this pull request to stack #6233 September 16, 2026 14:06
@iscekic
iscekic merged commit 1c7591f into main Sep 16, 2026
62 checks passed
@iscekic
iscekic deleted the kwf/approve-from-the-watch-cfaa branch September 16, 2026 15:42
@iscekic iscekic removed the human-ready The PR is ready for human review. label 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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants