Repository navigation
Conversation
…he event boundary The message.updated boundary now validates the identity, role and time the SDK reads, and the session's streaming check reads the assistant completion time defensively so a stored row without it cannot throw inside the transcript atoms. A permission request keeps patterns, metadata and always only when they parse, so a request whose scope cannot be read is not forwarded. Tests cover both events, the mirrored replay schema, and a stored assistant row with no readable time.
|
bot: Local web run at head 1fac735, Chromium against the dev stack with a session fixture serving the probe sessions' live events. Both captures come from the app with a seeded fake account on this VM.
Coverage limit: captured on the web client; the mobile consent card shares this SDK event path and was not captured. |
Code Review SummaryStatus: 1 Issue Found | Recommendation: Merge Executive SummaryThe three previous suggestions are addressed - the notice now announces itself, the atom-identity test returns fresh arrays, and the composer test mounts an answerable session - but the new unconditional announcement duplicates the Android live region, so TalkBack speaks the notice twice. Assumptions: Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (4 changed files)
Fix these issues in Kilo Cloud Previous Review Summaries (5 snapshots, latest commit 65c842f)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 65c842f)Status: 3 Issues Found | Recommendation: Merge Executive SummaryThe unreadable-permission path is correct end to end - normalizer event, per-id tracking, clearing on reply/reconnect/supersede, and rendering on web and mobile - and the only new items are an accessibility gap in the mobile notice and two tests that do not exercise the behavior they claim. Assumptions: Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (108 files)
Previous review (commit b863b86)Status: 1 Issue Found | Recommendation: Merge Executive SummaryThe replay seam now rejects stored rows the live boundary would reject, so the unreadable- Assumptions: Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (7 files)
Fix these issues in Kilo Cloud Previous review (commit d86442f)Status: 4 Issues Found | Recommendation: Address before merge Executive SummaryThe new completion-time fallback keeps a stored row from throwing inside the SDK atom but still hands it to transcript renderers that read Assumptions: Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (7 files)
Previous reviewStatus: 3 Issues Found | Recommendation: Address before merge Executive SummaryA malformed permission scope still disappears silently, leaving the CLI waiting with no consent card, and the replay mirror added by this PR still has no runtime consumer. Assumptions: Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (7 files)
Fix these issues in Kilo Cloud Previous review (commit 9df0612)Status: 5 Issues Found | Recommendation: Address before merge Executive SummaryThe event boundary correctly tightens message and permission parsing, but a malformed permission scope now disappears silently while the CLI waits, and one bad message timestamp drops the entire transcript row. Assumptions: Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (7 files)
Reviewed by deepseek-flash-latest · Input: 45.6K · Output: 10.4K · Cached: 654.5K Review guidance: REVIEW.md from base branch |
The role cases now include a valid time so each one exercises the role field.
|
bot: Re-ran the same local Chromium probe at head |
… schema Replay hands a stored page straight to the chat processor, so every replay path now checks each row against the same message schema the live wire uses. A row that does not satisfy it is skipped together with its parts, and the rest of the page is still emitted. The streaming check reads the completion stamp directly: every row that enters storage satisfies the message contract.
|
bot: Screenshots from the local end-to-end run at the current head.
Both captures are scoped to the conversation pane of the local web app, signed in as an owned test account on the local stack; no navigation or account chrome is included. |
|
Changes requested: do not drop an unreadable permission request silently.
|
|
bot: Added at head
Local end-to-end on this VM: a relayed request that cannot be read shows the notice with no card for it, a readable request beside it still renders its card, and answering the unreadable request clears the notice while the card stays. The same sequence was repeated on the Android dev client against the same stack. |
|
bot: Captures from the local end-to-end run at head
|
| const announcement = t('agentChat.permissionCard.unreadableBody'); | ||
|
|
||
| useEffect(() => { | ||
| announceForA11y(announcement); |
There was a problem hiding this comment.
SUGGESTION: Android now announces this notice twice
The notice sets accessibilityLiveRegion="polite" (line 30) and also announces imperatively here, on every platform. On Android both channels fire: TalkBack speaks the polite live-region text when the node is added and then speaks the same text again from announceForAccessibility. The repository's shared contract avoids exactly this by using one channel per platform - status-announcement.ts:6-11 documents "One channel per platform prevents double announcements" and useStatusAnnouncement only calls announceForA11y when Platform.OS === 'ios' (status-announcement.ts:50). Gate this call the same way (keep the live region for Android, announce only on iOS).







Evidence
Evidence: demonstrated — on the local stack I relayed session events and observed both surfaces render as designed: a request the app presents as a notice appears in the web client and on the Android dev client, a request it presents as a consent card still renders with its patterns and command, and answering the notice's request clears it.
Change
packages/cloud-agent-sdknow has a place for a permission request that the app cannot present as a decision, and both clients show it as a notice.permission.askedcontract is unchanged for a request that parses: it still becomes the consent card with the scope it declared.permission.unreadableevent carrying its request id; the transport groups it with the other pending-interaction events while the connected snapshot is pending.permission.repliedor on reconnect, and clears it when a presentable request for the same id arrives. The session manager exposes the list asatoms.unreadablePermissions.PermissionUnreadableNoticeabove the consent-card slot; mobile renders the same notice above the blocking-card region, with its copy in all 87 catalogs. Both name the situation and point the reader at the app or the CLI. The composer stays usable.message.updatedand replay handling from the earlier commits is unchanged.Tests:
normalizer.test.tsfor the boundary,service-state.test.tsfor tracking and clearing,session-manager.test.tsfor the atom,PermissionUnreadableNotice.test.tsfor the web render,permission-unreadable-notice.mounted.test.tsxandsession-detail-content.test.tsfor mobile.End-to-end verification
Run on the drain work VM, 2026-10-24, at head
9afd18244f5c5a1300ced80a1b33b9d79cb9490f.Stack, all local: web on 3000 (
pnpm dev:start nextjs cloudflare-session-ingest), Postgres fromdev/docker-compose.yml,pnpm drizzle migrate, and Metro on 8081 for the Android dev client.Fixture, on an owned test account created only in the local database: a session row written through the worker's own ingest endpoint, plus a local stand-in for a remote CLI that opens the same relay websocket, heartbeats the session, and emits the session's events. The browser's real user-web connection, the relay Durable Object, the shipped client bundles, and the real session state all run unchanged; only the agent end is local. Like the real CLI, the stand-in replays the requests it is still waiting on after a subscribe.
Probes:
Message, attachment and stop controls present)Limits: the answer transition was observed in the web client; mobile shares the same session state and atoms, and its clearing is covered by the state tests and the screen-level test. The device run observed the notice and the consent card together after a resubscribe, which is the path the relay takes for a still-pending request.
Production prerequisites
None.
Proxy answers
The owner's proxy gave these answers in the owner's place.
Answer: Match the CLI wire contract
Confidence: 0.65 · Precedent: Fix the root cause and the class of the issue: session 00eb2a1d3e4b, "if anything repeats the same mistake over and over again, fix the class". Every reader of time and role is covered at the single boundary, which matches the CLI contract.
Answer: Require all three (fail closed)
Confidence: 0.60 · Precedent: Accept real security fixes even when they cost some behavior: session fd4be8a3d43b, where the token-scheme and attachment-reaper fixes were approved. The brief also asks to report unparseable scope as malformed instead of forwarding empty values.
Answer: Drop silently
Confidence: 0.60 · Precedent: Reject changes that add new product policy, and keep the change to the brief's scope: session fd4be8a3d43b and PR fix(payments): refine callback response contracts #8666, "limit the change". Malformed events are handled the same way as every other malformed event, and any existing diagnostic logging for them stays in place.
Answer: Mirror into the replay schema
Confidence: 0.60 · Precedent: Fix the class on every affected surface: session 75f8833fe8c1, "fix this for all shared services model pickers on all surfaces"; the part schema is already mirrored there, so keep one shape model.
Answer: Stay usable (Recommended)
Confidence: 0.70 · Precedent: Choose the simplest design that reaches the goal: session a30dc8dc131a, owner typed "Simplest please"
Answer: Keep dropping it (Recommended)
Confidence: 0.65 · Precedent: Choose the simplest design that reaches the goal: session 1f0202ee8c71, "What's the simplest solution that gets us to our goal?"
Answer: No, transcript notice only (Recommended)
Confidence: 0.70 · Precedent: Do the least work possible with no drop in quality: session 7f3470e0ba6f, "do the least amount of work possible, with no drop in quality"
Answer: Surface a notice (Recommended)
Confidence: 0.85 · Precedent: Treat human PR actions as authoritative (session fd4be8a3d43b: owner's PR actions override earlier automated decisions); the owner's changes-requested comment supersedes the earlier 'drop silently' answer