Skip to content

fix(composer): recover after error and allow attachment-only send - #5319

Merged
iscekic merged 10 commits into
mainfrom
composer-recovery-28e5
Aug 18, 2026
Merged

fix(composer): recover after error and allow attachment-only send#5319
iscekic merged 10 commits into
mainfrom
composer-recovery-28e5

Conversation

@iscekic

@iscekic iscekic commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes three composer defects without a rewrite.

For the user: after Assistant request failed, the composer unlocks so you can type and send again in the same session; the red banner stays until the next successful send. A disabled composer on iOS no longer opens writing tools or a selection menu. A ready attachment with no text now sends.

For the product manager: the composer now recovers from an assistant error instead of dead-ending the session, and attachment-only sends work end to end. An empty prompt with no attachments stays rejected.

For the maintainer: the composer disabled state now derives from real locks only (resolveSessionComposerDisabled — read-only, transport canSend, loading, blocking interaction, missing model), dropping the Boolean(error) lock. The iOS TextInput sets contextMenuHidden and pointerEvents="none" when not editable. Attachment-only send is enabled by readyAttachmentsCount in canSend, and the backend accepts an empty prompt plus attachments across three gates: the send schemas (send-only clones plus rejectEmptyPromptWithoutAttachments), the wrapper createPromptHandler, and admitSubmittedMessage. Prepare and new-session paths keep .min(1).

Verification

  • pnpm --filter kilo-app test — 4525 passed
  • pnpm --filter cloud-agent-next test — 2548 passed, 3 skipped
  • pnpm --filter web test -- src/routers/cloud-agent-next-send-schema.test.ts — 8 passed
  • pnpm --filter kilo-app typecheck, pnpm --filter cloud-agent-next typecheck, pnpm --filter web typecheck — pass
  • pnpm --filter kilo-app lint, pnpm --filter cloud-agent-next lint, pnpm --filter web lint — 0 warnings, 0 errors
  • pnpm -w exec oxfmt --check on changed files — pass

Automated E2E (bot-e2e) covers the three runtime behaviors on iOS.

Visual Changes

N/A — behavior-only changes; no visual redesign. The red banner, composer layout, and send button appearance are unchanged.

Reviewer Notes

  • The three backend gates (schema, wrapper, admission) must all accept an empty prompt only when attachment or part files are present; an empty prompt with no attachments stays rejected everywhere.
  • Prepare and new-session paths intentionally keep .min(1); only follow-up send is relaxed.
  • kilo-chat and web ChatInput are unchanged.

Human steps

No human step is needed. No migration, secret, flag, or deploy-order change.

Resolve composer disabled state from real locks only: read-only, transport send, loading, blocking interaction, and missing model. The assistant error banner no longer locks the input, so the user can type and send again after a failed request.
Set contextMenuHidden and pointerEvents="none" on the composer TextInput when it is not editable, so a disabled field does not open writing tools or a selection menu on iOS.
Enable send when there is text or at least one uploaded attachment. handleSend no longer requires non-empty text, so an empty draft with a ready attachment sends prompt: '' plus the attachment payload.
Add send-only prompt clones and a rejectEmptyPromptWithoutAttachments refine so a follow-up send with an empty prompt and attachment files parses, while prepare still requires a prompt.
The prompt handler now accepts an empty prompt when attachments or parts are present, so materializePromptAttachments runs for attachment-only sends.
admitSubmittedMessage admits an empty prompt when attachment files are present and keeps No prompt provided for a prompt with no attachments.
Add a send-only payload schema so baseSendMessageNextSchema accepts an empty prompt with files while prepare initialPayload still requires a prompt.
@iscekic iscekic self-assigned this Aug 18, 2026
@iscekic
iscekic requested a review from jeanduplessis August 18, 2026 02:59
@kilo-code-bot

kilo-code-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of the send-only schema .extend() refactor across web and cloud-agent-next; empty-prompt-with-attachments still gates at the input refine, and prepare/new-session .min(1) paths are unchanged.

Files Reviewed (2 files)
  • apps/web/src/routers/cloud-agent-next-schemas.ts
  • services/cloud-agent-next/src/router/schemas.ts
Previous Review Summary (commit d122fe2)

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

Previous review (commit d122fe2)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Reviewed all 18 changed files across the mobile composer, web tRPC schemas, and the cloud-agent-next worker (router, message queue, wrapper); the three-gate empty-prompt-with-attachments contract is implemented consistently and the composer unlock/iOS input hardening changes are sound.

Coverage notes:

  • Composer disabled-state extraction (resolveSessionComposerDisabled) is semantic-preserving apart from the intentional removal of the Boolean(error) lock; all inputs used at the call site match the helper's contract, and error remains in use for the failure banner.
  • handleSend no longer guards on !trimmed, but canSend now requires real text (hasText is trim-based) or a ready attachment, and parseChatComposerSubmission('') deterministically falls through to a prompt send with the wire payload — attachment-only send is airtight, and text+attachment and text-only paths are unchanged.
  • Backend gates agree: web baseSendMessageNextSchema refine, service SendMessageV2/SendMessageInput rejectEmptyPromptWithoutAttachments, admitSubmittedMessage, and the wrapper prompt handler all accept an empty/whitespace prompt only when attachment or legacy images files are present; the handler boundary normalizes images into attachments before admission, so the queue's attachments-only check is consistent. Prepare/new-session paths correctly keep .min(1).
  • No memory-leak concerns: no new listeners, timers, or subscriptions in runtime code; test-only ref-slot stubs are reset in beforeEach and the mounted renderer is unmounted.
Files Reviewed (18 files)
  • apps/mobile/src/components/agents/chat-composer-attachment-send.test.ts
  • apps/mobile/src/components/agents/chat-composer-input-row.mounted.test.tsx
  • apps/mobile/src/components/agents/chat-composer-input-row.tsx
  • apps/mobile/src/components/agents/chat-composer-input-state.test.ts
  • apps/mobile/src/components/agents/chat-composer-input-state.ts
  • apps/mobile/src/components/agents/chat-composer.test.ts
  • apps/mobile/src/components/agents/chat-composer.tsx
  • apps/mobile/src/components/agents/session-composer-disabled.test.ts
  • apps/mobile/src/components/agents/session-composer-disabled.ts
  • apps/mobile/src/components/agents/session-detail-content.tsx
  • apps/web/src/routers/cloud-agent-next-schemas.ts
  • apps/web/src/routers/cloud-agent-next-send-schema.test.ts
  • services/cloud-agent-next/src/router/schemas.test.ts
  • services/cloud-agent-next/src/router/schemas.ts
  • services/cloud-agent-next/src/session/session-message-queue.test.ts
  • services/cloud-agent-next/src/session/session-message-queue.ts
  • services/cloud-agent-next/test/unit/wrapper/server.test.ts
  • services/cloud-agent-next/wrapper/src/server.ts

Reviewed by grok-4.6 · Input: 126K · Output: 9K · Cached: 407.8K

Review guidance: REVIEW.md from base branch main

Replace hand-copied send-only payload schemas with .extend derivations in cloud-agent-next and web. The .extend override drops .min(1) on prompt for the send-only types while the prepare types stay text-required.
@iscekic
iscekic merged commit 9525e06 into main Aug 18, 2026
18 checks passed
@iscekic
iscekic deleted the composer-recovery-28e5 branch August 18, 2026 12:40
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