Skip to content

fix(cloud-agent-sdk): treat unknown CLI capabilities as supported - #6662

Merged
iscekic merged 12 commits into
mainfrom
kwf/app-cli-capability-default-yes-a3fb
Sep 26, 2026
Merged

iscekic merged 12 commits into
mainfrom
kwf/app-cli-capability-default-yes-a3fb

Conversation

@iscekic

@iscekic iscekic commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Changelog for users

  • Remote CLI sessions show the attachment (paperclip) control before the CLI reports its capabilities.
  • The attachment control disappears once the CLI explicitly reports it cannot receive files.
  • Files can be shared to a connected CLI destination while its capability is unknown.
  • Read-only sessions keep the attachment control hidden.

Changelog for maintainers

  • Remote sessions report the attachments gate as supported while capabilities.attachments is unknown; only an explicit false downgrades it.
  • cloud-agent stays supported and read-only stays unsupported; sending attachment parts to a non-remote or explicitly incapable session is still rejected.
  • A new cliCapabilitySupported(value) helper (value !== false) backs both the gate and the send-time check.
  • Reconciliation is intact: onResolved and onTransportCapabilitiesChange both recompute, so a heartbeat or sessions.list negative applies immediately.
  • Mobile gates follow the same rule: remote-spawn file admission, clone/import sessionClone, and share-to-CLI admission.
  • The share gate sheet treats a missing capability row as capable and falls back to true for unknown session ids.
  • Review first recomputeSupportsAttachments and the send guard in the session manager, then the mobile !== false conversions.
  • JSDoc in the SDK schemas, types, session, transport, and CLI transport now describes the optimistic gate.

E2E proof

Owner request

Surface: the mobile app (apps/mobile) and the cloud-agent SDK (packages/cloud-agent-sdk).

A capability gate that depends on CLI support must default to YES. Today it defaults to NO, so a feature disappears until the CLI advertises it.

Evidence:

  • packages/cloud-agent-sdk/src/session-manager.ts:880 creates supportsAttachmentsAtom as atom(false).
  • recomputeSupportsAttachments (:1468-:1485) sets true for cloud-agent, and currentCapabilities?.attachments === true for remote. Every other remote state (absent, false, mid-reconnect) sets false.
  • apps/mobile/src/components/agents/session-detail-content.tsx:2298 passes that atom to attachmentsEnabled, so the paperclip is absent until the CLI reports the capability.

Requirements:

  • Optimistic default: while the CLI capability is unknown, the gate reports supported.
  • Downgrade only on an explicit negative. A heartbeat or sessions.list row that says attachments === false sets the gate to false.
  • A read-only session stays unsupported.
  • Apply the same rule to every gate in this file that reads a CLI capability. Attachments is one example, not the whole set.
  • The downgrade must still take effect as soon as the CLI reports it. Do not lose the reconciliation.

Proof: unit tests for unknown -> true, explicit false -> false, cloud-agent -> true, read-only -> false. Then one live proof on the platform you choose: open a remote session whose CLI has not yet reported capabilities, and show the attachment control present.

E2E proof

/home/igor_kilocode_ai/.local/share/kwf/sections/app-cli-capability-default-yes-a3fb/e2e-mobile-app/device.log
e2e-slot: slot-1 already holds this worktree; reusing it
e2e-slot: skipping the appium sweep: device-host: xcrun=absent adb=absent sdk=none; no xcrun on PATH (/home/igor_kilocode_ai/.bun/bin:/home/igor_kilocode_ai/.nvm/versions/node/v24.14.1/bin:/usr/local/
e2e-slot: slot-1 already holds this worktree; reusing it
e2e-slot: skipping the appium sweep: device-host: xcrun=absent adb=absent sdk=none; no xcrun on PATH (/home/igor_kilocode_ai/.bun/bin:/home/igor_kilocode_ai/.nvm/versions/node/v24.14.1/bin:/usr/local/
e2e-slot: slot-1 already holds this worktree; reusing it
e2e-slot: skipping the appium sweep: device-host: xcrun=absent adb=absent sdk=none; no xcrun on PATH (/home/igor_kilocode_ai/.bun/bin:/home/igor_kilocode_ai/.nvm/versions/node/v24.14.1/bin:/usr/local/
e2e-slot: slot-1 already holds this worktree; reusing it
e2e-slot: skipping the appium sweep: device-host: xcrun=absent adb=absent sdk=none; no xcrun on PATH (/home/igor_kilocode_ai/.bun/bin:/home/igor_kilocode_ai/.nvm/versions/node/v24.14.1/bin:/usr/local/

Open findings (not fixed here)

  • [e1] Open a remote CLI session in the mobile app whose CLI has not yet reported capabilities (its sessions.list/heartbeat row has no attachments field): the composer attachment (paperclip) control is present.: reported skip, so nothing proves it (No mobile device on this linux host: my orient run
  • [e2] With that remote session open, let the CLI report capabilities.attachments: false (or open a session whose row says false): the paperclip disappears and sending a file is refused with the 'can't receive files' error.: reported skip, so nothing proves it (Same device absence ('ANDROID CLAIMED
  • [e3] Open a read-only session: no paperclip, and an attempted attachment send is refused.: reported skip, so nothing proves it (Same device absence ('IOS BOOTED none', e2e-cli/orient-verify.log): the read-only gate and its refusal were exercised only in-process (e2e-cli/sdk-gate-verify.j)
  • [e4] Share a file to a connected CLI destination whose capability row is absent: the share commits without the 'can't receive files' alert.: reported skip, so nothing proves it (Same device absence (e2e-cli/e1-device-attempt.log): the share-destination admission for a row absent from the capability
  • the '## E2E proof' section shows no screenshot, and this change has a visible surface

Surface: the mobile app (apps/mobile) and the cloud-agent SDK (packages/cloud-agent-sdk).

A capability gate that depends on CLI support must default to YES. Today it defaults to NO, so a feature disappears until the CLI advertises it.

Evidence:
- `packages/cloud-agent-sdk/src/session-manager.ts:880` creates `supportsAttachmentsAtom` as `atom(false)`.
- `recomputeSupportsAttachments` (`:1468`-`:1485`) sets `true` for `cloud-agent`, and `currentCapabilities?.attachments === true` for `remote`. Every other remote state (absent, false, mid-reconnect) sets `false`.
- `apps/mobile/src/components/agents/session-detail-content.tsx:2298` passes that atom to `attachmentsEnabled`, so the paperclip is absent until the CLI reports the capability.

Requirements:
- Optimistic default: while the CLI capability is unknown, the gate reports supported.
- Downgrade only on an explicit negative. A heartbeat or `sessions.list` row that says `attachments === false` sets the gate to false.
- A `read-only` session stays unsupported.
- Apply the same rule to every gate in this file that reads a CLI capability. Attachments is one example, not the whole set.
- The downgrade must still take effect as soon as the CLI reports it. Do not lose the reconciliation.

Proof: unit tests for unknown -> true, explicit false -> false, `cloud-agent` -> true, `read-only` -> false. Then one live proof on the platform you choose: open a remote session whose CLI has not yet reported capabilities, and show the attachmen
@iscekic
iscekic marked this pull request as draft September 23, 2026 21:06
@kilo-code-bot

kilo-code-bot Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The only incremental change since 55d11fd is a test-only refactor in use-remote-spawn-dispatch.test.ts; both refreshed-instance refetchInstances stubs are extracted to a liveInstances const and restored to the file's existing () => Promise.resolve(...) pattern with an eslint-disable, resolving identical values with no behavioral change and no new risk.

Files Reviewed (1 file)
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.test.ts
Previous Review Summaries (6 snapshots, latest commit 55d11fd)

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

Previous review (commit 55d11fd)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The only change since the previous review (55d11fd) is a test-only refactor in use-remote-spawn-dispatch.test.ts; both refetchInstances stubs moved from () => Promise.resolve({...}) to async () => ({...}) with identical resolved values, so there is no behavioral change and no new risk.

Files Reviewed (1 file)
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.test.ts

Previously reported findings remain addressed: the spawn dispatch now re-runs file and clone admission against the refreshed live instance before committing (85073ce), and the SDK send guard requires supportsRemoteAttachmentParts (be77704).

Previous review (commit be77704)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The two follow-up fixes are correct and consistent with the existing gates: the spawn dispatch re-runs file and clone admission against the refreshed live instance before committing, and the SDK send guard now requires supportsRemoteAttachmentParts, matching the supportsAttachments gate's condition.

Files Reviewed (4 files)
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.test.ts
  • packages/cloud-agent-sdk/src/session-manager.ts
  • packages/cloud-agent-sdk/src/session-manager.test.ts

Previous review (commit 4d93db2)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The net PR is scoped to the CLI-capability change: unknown capabilities.attachments / sessionClone are treated as supported across the cloud-agent SDK and the mobile gates, and the new supportsRemoteAttachmentParts opt-in keeps web's remote sessions unsupported, resolving the prior web-consumer concern.

Files Reviewed (18 files)
  • apps/mobile/src/components/agents/mobile-session-manager-helpers.ts
  • apps/mobile/src/components/agents/mobile-session-manager.ts
  • apps/mobile/src/components/agents/new-session-screen-body.tsx
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.test.ts
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts
  • apps/mobile/src/components/share/share-cli-admission.test.ts
  • apps/mobile/src/components/share/share-cli-admission.ts
  • apps/mobile/src/components/share/share-gate-sheet.tsx
  • apps/mobile/src/lib/composer-auto-send.ts
  • apps/mobile/src/lib/remote-spawn-admission.test.ts
  • apps/mobile/src/lib/remote-spawn-admission.ts
  • packages/cloud-agent-sdk/src/cli-live-transport.ts
  • packages/cloud-agent-sdk/src/schemas.ts
  • packages/cloud-agent-sdk/src/session-manager.test.ts
  • packages/cloud-agent-sdk/src/session-manager.ts
  • packages/cloud-agent-sdk/src/session.ts
  • packages/cloud-agent-sdk/src/transport.ts
  • packages/cloud-agent-sdk/src/types.ts

Previous review (commit e948963)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of the only files changed after 7caa120: services/gastown/src/gastown.worker.ts now re-enters the Kilo JWT + per-town ownership chain on the /api/towns/:townId/container/* control-plane routes (resolving the prior CRITICAL) and town-container.test.ts asserts the resulting 401.

Files Reviewed (2 files)
  • services/gastown/src/gastown.worker.ts
  • services/gastown/test/integration/town-container.test.ts

Previous review (commit 7caa120)

Status: 1 Issues Found | Recommendation: Address before merge

Executive Summary

The incremental commits add a mobile-only supportsRemoteAttachmentParts opt-in (fixing the web gate raised in the kwf-audit comment) and bundle an automated backend-gate repair that removes per-town authorization from the gastown container control-plane routes.

Overview

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

CRITICAL

File Line Issue
services/gastown/src/gastown.worker.ts 926 /container/ skip list bypasses kiloAuthMiddleware + townAuthMiddleware, dropping per-town authorization on the container control-plane routes

Fix these issues in Kilo Cloud

Files Reviewed (22 files)
  • apps/mobile/src/components/agents/mobile-session-manager.ts
  • packages/cloud-agent-sdk/src/session-manager.test.ts
  • packages/cloud-agent-sdk/src/session-manager.ts
  • services/gastown/src/dos/Agent.do.ts
  • services/gastown/src/dos/Town.do.ts
  • services/gastown/src/gastown.worker.ts - 1 issue
  • services/gastown/src/handlers/town-container.handler.ts
  • services/gastown/src/util/parse-json-body.util.ts
  • services/gastown/test/integration/awaiting-approval.test.ts
  • services/gastown/test/integration/convoy-dag.test.ts
  • services/gastown/test/integration/http-api.test.ts
  • services/gastown/test/integration/mayor-idle.test.ts
  • services/gastown/test/integration/pr-poll-errors.test.ts
  • services/gastown/test/integration/reconciler.test.ts
  • services/gastown/test/integration/review-failure.test.ts
  • services/gastown/test/integration/rig-alarm.test.ts
  • services/gastown/test/integration/rig-do.test.ts
  • services/gastown/test/integration/town-container.test.ts
  • services/gastown/test/integration/town-deletion.test.ts
  • services/security-auto-analysis/vitest.integration.config.ts
  • services/session-ingest/src/ingest/validate-oversized.test.ts
  • services/session-ingest/src/ingest/validate.test.ts

Previous review (commit fac3728)

Status: No Issues Found | Recommendation: Merge

Executive Summary

This PR flips the CLI capability gates from fail-closed (=== true) to optimistic (!== false / === false) across the cloud-agent SDK and mobile app. I verified every changed gate and call site is internally consistent, and the send-time guard, reconciliation on onResolved/onTransportCapabilitiesChange, and read-only behavior are all preserved.

Files Reviewed (17 files)
  • apps/mobile/src/components/agents/mobile-session-manager-helpers.ts
  • apps/mobile/src/components/agents/new-session-screen-body.tsx
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.test.ts
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts
  • apps/mobile/src/components/share/share-cli-admission.test.ts
  • apps/mobile/src/components/share/share-cli-admission.ts
  • apps/mobile/src/components/share/share-gate-sheet.tsx
  • apps/mobile/src/lib/composer-auto-send.ts
  • apps/mobile/src/lib/remote-spawn-admission.test.ts
  • apps/mobile/src/lib/remote-spawn-admission.ts
  • packages/cloud-agent-sdk/src/cli-live-transport.ts
  • packages/cloud-agent-sdk/src/schemas.ts
  • packages/cloud-agent-sdk/src/session-manager.test.ts
  • packages/cloud-agent-sdk/src/session-manager.ts
  • packages/cloud-agent-sdk/src/session.ts
  • packages/cloud-agent-sdk/src/transport.ts
  • packages/cloud-agent-sdk/src/types.ts

Notes

The optimistic default is intentional per the PR requirements: files can be admitted while capabilities.attachments is unknown, and only an explicit false downgrades the gate. The SDK send guard still rejects non-remote sessions and sessions that explicitly reported false, and recomputeSupportsAttachments recomputes on both onResolved and onTransportCapabilitiesChange, so a heartbeat/sessions.list negative applies immediately. All production call sites that read a CLI capability were converted consistently; remaining === true occurrences are in test fixtures that pass the boolean directly.


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

Review guidance: REVIEW.md from base branch main

@iscekic
iscekic marked this pull request as ready for review September 23, 2026 22:19
@iscekic

iscekic commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator Author

kilo-review — independent audit of the published diff.

Status: 1 Issues

File Line Issue
packages/cloud-agent-sdk/src/session-manager.ts 1499 The shared supportsAttachments gate is also consumed by the web CloudChatPage, whose send path only supports the cloud-only attachments field, so a remote CLI session with unknown capabilities now shows the paperclip and any attached file makes the send fail with 'Only Cloud Agent sessions support attachments'.

@iscekic
iscekic marked this pull request as draft September 23, 2026 22:49
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 25, 2026
@iscekic
iscekic marked this pull request as ready for review September 25, 2026 13:49
Comment thread services/gastown/src/gastown.worker.ts Outdated
@iscekic iscekic removed the human-ready The PR is ready for human review. label Sep 25, 2026
The /container/ entry in the /api/towns/:townId/* skip list let every
Town Container control-plane route (agents/start, agents/:id/stop,
agents/:id/message, agents/:id/status, agents/:id/stream-ticket, health,
pty) bypass kiloAuthMiddleware, adminAuditMiddleware and
townAuthMiddleware. The handlers proxy straight to the container control
server and check no authorization of their own, so any principal that
clears Cloudflare Access could drive another tenant's container by
supplying its townId. CF Access authenticates the caller but does not
enforce town ownership.

Drop the skip and return the middleware response instead of awaiting it,
so an unauthenticated caller gets the middleware 401 instead of a dropped
response. Update the container route comment and the two integration
tests that asserted an unauthenticated request reached the body validator.
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 25, 2026
The gastown auth and Durable Object fixes, the session-ingest test
repair, and the security-auto-analysis integration config came from an
unrelated backend gate repair. They do not belong to a cloud-agent-sdk
capability change.

Reverts those trees to the branch merge base (8e59fe6). The same
gastown fix is present in #6689 and #6580.
Comment thread apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts
Comment thread packages/cloud-agent-sdk/src/session-manager.ts Outdated
The file and clone checks ran against the press-time row before the
refetch resolved the live row. A rebooted host can come back on a new
connectionId and report an explicit refusal the press-time row did not,
so the spawn could use a row that now refuses the file payload or the
clone source. Both checks now run again against the live row before the
spawn commits; the attempt was already admitted, so a refusal fails it
and re-arms the abandon guard.
The send guard checked the session type and the CLI capability, but not
the consumer's declaration that it can deliver remote attachment parts.
The UI gate disables the attachment control without that declaration, so
a caller that supplied attachmentParts could still have them forwarded.
The guard now requires config.supportsRemoteAttachmentParts, the same
condition the gate uses.
The two new stubs returned Promise.resolve from a plain arrow, which the
repo's promise rules reject (promise-function-async and
prefer-await-to-then). An async arrow satisfies both without the disable
comment the older stubs needed.
@iscekic
iscekic marked this pull request as draft September 25, 2026 19:12
@iscekic
iscekic marked this pull request as ready for review September 25, 2026 19:12
An async arrow trips require-await in the mobile lint config, and a bare
Promise.resolve arrow trips promise-function-async and
prefer-await-to-then. The file's established single-line stub with the
disable comment satisfies all three; the refreshed list is hoisted to a
const so the stub stays on one line.
@iscekic

iscekic commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator Author

Audit finding (packages/cloud-agent-sdk/src/session-manager.ts, shared supportsAttachments gate on web): already fixed in 8155e4b (hardened in be77704). A remote session reports attachments supported only when the consumer sets supportsRemoteAttachmentParts: true. Only the mobile session manager sets it, so web CloudChatPage does not show the paperclip for a remote CLI session, and the send guard refuses attachmentParts from a consumer that did not declare it.

@iscekic
iscekic merged commit a9a8938 into main Sep 26, 2026
30 checks passed
@iscekic
iscekic deleted the kwf/app-cli-capability-default-yes-a3fb branch September 26, 2026 01:00
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