From c9ffb952445e68bfce00f22b2bf6543a62f192ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 23 Jul 2026 04:53:05 +0200 Subject: [PATCH 01/14] ci(kilo-app): add mobile test job and gate release on validation --- .github/workflows/kilo-app-ci.yml | 27 ++++++++++++++++++++++++++ .github/workflows/kilo-app-release.yml | 5 ++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/.github/workflows/kilo-app-ci.yml b/.github/workflows/kilo-app-ci.yml index f532b28d5c..f5cf650bcb 100644 --- a/.github/workflows/kilo-app-ci.yml +++ b/.github/workflows/kilo-app-ci.yml @@ -17,6 +17,7 @@ on: - 'apps/web/src/routers/**' - 'pnpm-lock.yaml' - '.github/workflows/kilo-app-ci.yml' + workflow_call: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -128,3 +129,29 @@ jobs: - name: Check unused exports run: pnpm --filter kilo-app run check:unused + + test: + runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }} + timeout-minutes: 15 + steps: + - uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1 + with: + lfs: true + + - name: Setup pnpm + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + + - name: Setup Node + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version-file: '.nvmrc' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build trpc types + run: pnpm --filter @kilocode/trpc run build + + - name: Test + run: pnpm --filter kilo-app run test diff --git a/.github/workflows/kilo-app-release.yml b/.github/workflows/kilo-app-release.yml index 1cdff54306..c5bd8196d9 100644 --- a/.github/workflows/kilo-app-release.yml +++ b/.github/workflows/kilo-app-release.yml @@ -49,8 +49,11 @@ jobs: echo "should_build=true" >> "$GITHUB_OUTPUT" fi + validate: + uses: ./.github/workflows/kilo-app-ci.yml + build-and-submit: - needs: check-changes + needs: [check-changes, validate] if: needs.check-changes.outputs.should_build == 'true' && github.ref == 'refs/heads/main' runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }} timeout-minutes: 60 From 695837e5856e2558f4d25d274bc354aea3270646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 23 Jul 2026 06:14:36 +0200 Subject: [PATCH 02/14] feat(cloud-agent-sdk): extract packages/cloud-agent-sdk and migrate web consumers --- .oxlintrc.json | 11 + apps/web/package.json | 1 + .../cloud-agent-next/ChildSessionDrawer.tsx | 2 +- .../cloud-agent-next/ChildSessionSection.tsx | 2 +- .../cloud-agent-next/CloudAgentProvider.tsx | 2 +- .../cloud-agent-next/CloudChatPage.tsx | 4 +- .../ContextUsageIndicator.tsx | 2 +- .../cloud-agent-next/FeedbackDialog.tsx | 2 +- .../cloud-agent-next/MessageBubble.tsx | 2 +- .../cloud-agent-next/NewSessionPanel.tsx | 4 +- .../cloud-agent-next/PreparationDrawer.tsx | 2 +- .../cloud-agent-next/PreparationRow.tsx | 2 +- .../SessionStatusIndicator.tsx | 2 +- .../cloud-agent-next/SuggestionCard.tsx | 2 +- .../cloud-agent-next/delivery-badge.ts | 2 +- .../hooks/useActiveSessions.ts | 2 +- .../hooks/useSessionModels.test.ts | 2 +- .../hooks/useSessionModels.ts | 2 +- .../hooks/useSidebarSessions.test.ts | 2 +- .../hooks/useSidebarSessions.ts | 2 +- .../model-context-lengths.test.ts | 2 +- .../cloud-agent-next/model-context-lengths.ts | 2 +- .../preparation-phases.test.ts | 2 +- .../cloud-agent-next/preparation-phases.ts | 2 +- .../preparation-summary.test.ts | 2 +- .../cloud-agent-next/preparation-summary.ts | 4 +- apps/web/src/hooks/slash-command-selection.ts | 4 +- .../web/src/hooks/useSlashCommandSets.test.ts | 6 +- .../src/lib/cloud-agent-next/event-types.ts | 93 +-- apps/web/src/lib/cloud-agent/constants.ts | 5 +- apps/web/src/lib/images-schema.ts | 36 +- .../src/routers/cloud-agent-next-router.ts | 2 +- .../organization-cloud-agent-next-router.ts | 2 +- apps/web/src/types/opencode.gen.ts | 623 +----------------- packages/app-shared/package.json | 8 +- packages/app-shared/src/cloud-agent.ts | 9 + packages/app-shared/src/images-schema.ts | 35 + packages/app-shared/src/opencode.gen.ts | 623 ++++++++++++++++++ packages/cloud-agent-sdk/jest.config.mjs | 17 + packages/cloud-agent-sdk/package.json | 41 ++ .../src}/__fixtures__/autocommit.ts | 0 .../src}/__fixtures__/basic-streaming.ts | 0 .../src}/__fixtures__/child-sessions.ts | 0 .../src}/__fixtures__/helpers.ts | 2 +- .../src}/__fixtures__/index.ts | 0 .../src}/__fixtures__/interruption.ts | 0 .../message-delivery-exhausted.ts | 0 .../optimistic-user-text-reconciliation.ts | 0 .../src}/__fixtures__/real-session-excerpt.ts | 2 +- .../src}/__fixtures__/tool-use-cycle.ts | 0 .../src}/__fixtures__/types.ts | 2 +- .../cloud-agent-sdk/src}/array-utils.ts | 0 .../src}/base-connection.test.ts | 0 .../cloud-agent-sdk/src}/base-connection.ts | 0 .../src}/chat-processor.test.ts | 2 +- .../cloud-agent-sdk/src}/chat-processor.ts | 2 +- .../src}/cli-historical-transport.test.ts | 0 .../src}/cli-historical-transport.ts | 0 .../src}/cli-live-transport.test.ts | 0 .../src}/cli-live-transport.ts | 0 .../cloud-agent-sdk/src}/cli-model.ts | 0 .../src}/cloud-agent-connection.test.ts | 0 .../src}/cloud-agent-connection.ts | 2 +- .../src}/cloud-agent-transport.test.ts | 2 +- .../src}/cloud-agent-transport.ts | 0 .../src}/context-usage.test.ts | 2 +- .../cloud-agent-sdk/src}/context-usage.ts | 0 .../src}/create-session.test.ts | 0 .../cloud-agent-sdk/src}/create-session.ts | 0 packages/cloud-agent-sdk/src/event-types.ts | 92 +++ .../cloud-agent-sdk/src}/exit-session.test.ts | 0 .../cloud-agent-sdk/src}/exit-session.ts | 0 .../src}/fixture-runner.test.ts | 0 .../cloud-agent-sdk/src}/index.ts | 3 + .../cloud-agent-sdk/src}/message-id.ts | 0 .../cloud-agent-sdk/src}/normalizer.test.ts | 2 +- .../cloud-agent-sdk/src}/normalizer.ts | 2 +- .../cloud-agent-sdk/src}/part-utils.ts | 0 .../src}/preparation-attempts.test.ts | 0 .../src}/preparation-attempts.ts | 0 .../cloud-agent-sdk/src}/reducer.test.ts | 2 +- .../cloud-agent-sdk/src}/reducer.ts | 0 .../src}/remote-command-catalog.test.ts | 0 .../src}/remote-command-catalog.ts | 0 .../src}/remote-model-catalog.test.ts | 0 .../src}/remote-model-catalog.ts | 0 .../src}/remote-model-order.ts | 0 .../cloud-agent-sdk/src}/runtime.test.ts | 0 .../cloud-agent-sdk/src}/runtime.ts | 0 .../cloud-agent-sdk/src}/schemas.test.ts | 0 .../cloud-agent-sdk/src}/schemas.ts | 0 .../src}/service-state.test.ts | 2 +- .../cloud-agent-sdk/src}/service-state.ts | 2 +- .../src}/session-manager.test.ts | 2 +- .../cloud-agent-sdk/src}/session-manager.ts | 6 +- .../src}/session-phase.test.ts | 2 +- .../src}/session-routing.test.ts | 0 .../src}/session-transport.test.ts | 0 .../cloud-agent-sdk/src}/session.test.ts | 0 .../cloud-agent-sdk/src}/session.ts | 6 +- .../src}/storage/helpers.test.ts | 2 +- .../cloud-agent-sdk/src}/storage/helpers.ts | 2 +- .../src}/storage/jotai.test.ts | 2 +- .../cloud-agent-sdk/src}/storage/jotai.ts | 2 +- .../src}/storage/memory.test.ts | 2 +- .../cloud-agent-sdk/src}/storage/memory.ts | 2 +- .../cloud-agent-sdk/src}/storage/types.ts | 2 +- .../cloud-agent-sdk/src}/test-helpers.ts | 4 +- .../cloud-agent-sdk/src}/transport.ts | 4 +- .../cloud-agent-sdk/src}/types.ts | 6 +- .../src}/user-web-connection.test.ts | 0 .../src}/user-web-connection.ts | 0 packages/cloud-agent-sdk/tsconfig.json | 17 + pnpm-lock.yaml | 45 +- 114 files changed, 972 insertions(+), 825 deletions(-) create mode 100644 packages/app-shared/src/cloud-agent.ts create mode 100644 packages/app-shared/src/images-schema.ts create mode 100644 packages/app-shared/src/opencode.gen.ts create mode 100644 packages/cloud-agent-sdk/jest.config.mjs create mode 100644 packages/cloud-agent-sdk/package.json rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/__fixtures__/autocommit.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/__fixtures__/basic-streaming.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/__fixtures__/child-sessions.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/__fixtures__/helpers.ts (96%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/__fixtures__/index.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/__fixtures__/interruption.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/__fixtures__/message-delivery-exhausted.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/__fixtures__/optimistic-user-text-reconciliation.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/__fixtures__/real-session-excerpt.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/__fixtures__/tool-use-cycle.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/__fixtures__/types.ts (84%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/array-utils.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/base-connection.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/base-connection.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/chat-processor.test.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/chat-processor.ts (97%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/cli-historical-transport.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/cli-historical-transport.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/cli-live-transport.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/cli-live-transport.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/cli-model.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/cloud-agent-connection.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/cloud-agent-connection.ts (98%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/cloud-agent-transport.test.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/cloud-agent-transport.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/context-usage.test.ts (98%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/context-usage.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/create-session.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/create-session.ts (100%) create mode 100644 packages/cloud-agent-sdk/src/event-types.ts rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/exit-session.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/exit-session.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/fixture-runner.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/index.ts (97%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/message-id.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/normalizer.test.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/normalizer.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/part-utils.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/preparation-attempts.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/preparation-attempts.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/reducer.test.ts (96%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/reducer.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/remote-command-catalog.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/remote-command-catalog.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/remote-model-catalog.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/remote-model-catalog.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/remote-model-order.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/runtime.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/runtime.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/schemas.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/schemas.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/service-state.test.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/service-state.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/session-manager.test.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/session-manager.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/session-phase.test.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/session-routing.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/session-transport.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/session.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/session.ts (98%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/storage/helpers.test.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/storage/helpers.ts (97%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/storage/jotai.test.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/storage/jotai.ts (98%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/storage/memory.test.ts (99%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/storage/memory.ts (98%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/storage/types.ts (96%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/test-helpers.ts (94%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/transport.ts (97%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/types.ts (97%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/user-web-connection.test.ts (100%) rename {apps/web/src/lib/cloud-agent-sdk => packages/cloud-agent-sdk/src}/user-web-connection.ts (100%) create mode 100644 packages/cloud-agent-sdk/tsconfig.json diff --git a/.oxlintrc.json b/.oxlintrc.json index 9d78b380c2..df8f768bc0 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -210,6 +210,17 @@ "@typescript-eslint/no-unsafe-call": "off", "@typescript-eslint/no-unsafe-member-access": "off" } + }, + { + "files": ["packages/cloud-agent-sdk/src/**/*.ts"], + "rules": { + "@typescript-eslint/no-base-to-string": "off", + "@typescript-eslint/no-empty-object-type": "off", + "@typescript-eslint/no-redundant-type-constituents": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/unbound-method": "off" + } } ] } diff --git a/apps/web/package.json b/apps/web/package.json index 3d91180c77..fd1a5e42d6 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -48,6 +48,7 @@ "@kilocode/app-shared": "workspace:*", "@kilocode/auto-routing-contracts": "workspace:*", "@kilocode/cloud-agent-profile": "workspace:*", + "@kilocode/cloud-agent-sdk": "workspace:*", "@kilocode/db": "workspace:*", "@kilocode/encryption": "workspace:*", "@kilocode/event-service": "workspace:*", diff --git a/apps/web/src/components/cloud-agent-next/ChildSessionDrawer.tsx b/apps/web/src/components/cloud-agent-next/ChildSessionDrawer.tsx index fd0ee730f4..0e58c27f35 100644 --- a/apps/web/src/components/cloud-agent-next/ChildSessionDrawer.tsx +++ b/apps/web/src/components/cloud-agent-next/ChildSessionDrawer.tsx @@ -11,7 +11,7 @@ import { SheetHeader, SheetTitle, } from '@/components/ui/sheet'; -import type { ChildSessionHydrationState } from '@/lib/cloud-agent-sdk'; +import type { ChildSessionHydrationState } from '@kilocode/cloud-agent-sdk'; import { useManager } from './CloudAgentProvider'; import { MessageBubble } from './MessageBubble'; import { MessageErrorBoundary } from './MessageErrorBoundary'; diff --git a/apps/web/src/components/cloud-agent-next/ChildSessionSection.tsx b/apps/web/src/components/cloud-agent-next/ChildSessionSection.tsx index a79c2b72e8..6c4f2ad614 100644 --- a/apps/web/src/components/cloud-agent-next/ChildSessionSection.tsx +++ b/apps/web/src/components/cloud-agent-next/ChildSessionSection.tsx @@ -4,7 +4,7 @@ import { useState, type ReactNode } from 'react'; import { ChevronRight, ChevronDown, Bot, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; -import type { KiloSessionId } from '@/lib/cloud-agent-sdk'; +import type { KiloSessionId } from '@kilocode/cloud-agent-sdk'; import type { SubtaskPart, StoredMessage, ToolPart, Part } from './types'; import { isMessageStreaming, isToolPart } from './types'; import { MessageErrorBoundary } from './MessageErrorBoundary'; diff --git a/apps/web/src/components/cloud-agent-next/CloudAgentProvider.tsx b/apps/web/src/components/cloud-agent-next/CloudAgentProvider.tsx index d7d7d02204..7c921b0c47 100644 --- a/apps/web/src/components/cloud-agent-next/CloudAgentProvider.tsx +++ b/apps/web/src/components/cloud-agent-next/CloudAgentProvider.tsx @@ -15,7 +15,7 @@ import { type CloudAgentSessionId, type TransportSendPayload, type UserWebConnection, -} from '@/lib/cloud-agent-sdk'; +} from '@kilocode/cloud-agent-sdk'; import type { SendMessagePayload } from '@/lib/cloud-agent-next/cloud-agent-client'; import { CLOUD_AGENT_NEXT_WS_URL, SESSION_INGEST_WS_URL } from '@/lib/constants'; import { usePostHog } from 'posthog-js/react'; diff --git a/apps/web/src/components/cloud-agent-next/CloudChatPage.tsx b/apps/web/src/components/cloud-agent-next/CloudChatPage.tsx index b9090f40bd..df7f8a8456 100644 --- a/apps/web/src/components/cloud-agent-next/CloudChatPage.tsx +++ b/apps/web/src/components/cloud-agent-next/CloudChatPage.tsx @@ -8,7 +8,7 @@ import { useTRPC } from '@/lib/trpc/utils'; import { ArrowDown, GitBranch } from 'lucide-react'; import { v4 as uuidv4 } from 'uuid'; -import type { KiloSessionId } from '@/lib/cloud-agent-sdk'; +import type { KiloSessionId } from '@kilocode/cloud-agent-sdk'; import { useManager } from './CloudAgentProvider'; import { MobileSidebarToggle } from './MobileSidebarToggle'; import { ChatHeader } from './ChatHeader'; @@ -59,7 +59,7 @@ import type { MessageDeliveryState, PreparationAttempt, StoredMessage, -} from '@/lib/cloud-agent-sdk'; +} from '@kilocode/cloud-agent-sdk'; import type { WorkspaceTabId } from './terminal-tabs'; import type { TerminalStatus } from './useCloudAgentTerminal'; diff --git a/apps/web/src/components/cloud-agent-next/ContextUsageIndicator.tsx b/apps/web/src/components/cloud-agent-next/ContextUsageIndicator.tsx index cd05a23f7c..2f8568a1d7 100644 --- a/apps/web/src/components/cloud-agent-next/ContextUsageIndicator.tsx +++ b/apps/web/src/components/cloud-agent-next/ContextUsageIndicator.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; -import { calculateContextUsagePercentage } from '@/lib/cloud-agent-sdk/context-usage'; +import { calculateContextUsagePercentage } from '@kilocode/cloud-agent-sdk/context-usage'; type ContextUsageIndicatorProps = { contextTokens?: number; diff --git a/apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx b/apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx index aaafdef9a8..37132e8453 100644 --- a/apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx +++ b/apps/web/src/components/cloud-agent-next/FeedbackDialog.tsx @@ -16,7 +16,7 @@ import { DialogTrigger, } from '@/components/ui/dialog'; import { useManager } from './CloudAgentProvider'; -import type { ResolvedSession } from '@/lib/cloud-agent-sdk'; +import type { ResolvedSession } from '@kilocode/cloud-agent-sdk'; import type { StoredMessage } from './types'; import { isTextPart } from './types'; diff --git a/apps/web/src/components/cloud-agent-next/MessageBubble.tsx b/apps/web/src/components/cloud-agent-next/MessageBubble.tsx index 3cd4f7508c..65a098b496 100644 --- a/apps/web/src/components/cloud-agent-next/MessageBubble.tsx +++ b/apps/web/src/components/cloud-agent-next/MessageBubble.tsx @@ -4,7 +4,7 @@ import React, { useCallback } from 'react'; import { Scissors, Image, FileText, AlertCircle, Clock } from 'lucide-react'; import { TimeAgo } from '@/components/shared/TimeAgo'; import type { AssistantMessage } from '@/types/opencode.gen'; -import type { MessageDeliveryState } from '@/lib/cloud-agent-sdk'; +import type { MessageDeliveryState } from '@kilocode/cloud-agent-sdk'; import type { StoredMessage, Part, CompactionPart } from './types'; import { isUserMessage, diff --git a/apps/web/src/components/cloud-agent-next/NewSessionPanel.tsx b/apps/web/src/components/cloud-agent-next/NewSessionPanel.tsx index 4d145dce09..c72a939443 100644 --- a/apps/web/src/components/cloud-agent-next/NewSessionPanel.tsx +++ b/apps/web/src/components/cloud-agent-next/NewSessionPanel.tsx @@ -69,8 +69,8 @@ import { detectGitPlatform, } from '@/components/cloud-agent-next/utils/git-utils'; import type { AgentMode } from './types'; -import { formatSessionError } from '@/lib/cloud-agent-sdk'; -import { generateMessageId } from '@/lib/cloud-agent-sdk/message-id'; +import { formatSessionError } from '@kilocode/cloud-agent-sdk'; +import { generateMessageId } from '@kilocode/cloud-agent-sdk/message-id'; import { useCloudAgentAttachmentUpload } from '@/hooks/useCloudAgentAttachmentUpload'; import { AttachmentPreviewStrip } from './AttachmentPreviewStrip'; import { diff --git a/apps/web/src/components/cloud-agent-next/PreparationDrawer.tsx b/apps/web/src/components/cloud-agent-next/PreparationDrawer.tsx index f6cedb9adb..d4021605bd 100644 --- a/apps/web/src/components/cloud-agent-next/PreparationDrawer.tsx +++ b/apps/web/src/components/cloud-agent-next/PreparationDrawer.tsx @@ -14,7 +14,7 @@ import type { PreparationAttempt, PreparationStepSnapshot, PreparationStepStatus, -} from '@/lib/cloud-agent-sdk'; +} from '@kilocode/cloud-agent-sdk'; import { StatusSpinner } from '@/components/shared/StatusSpinner'; import { cn } from '@/lib/utils'; import { useManager } from './CloudAgentProvider'; diff --git a/apps/web/src/components/cloud-agent-next/PreparationRow.tsx b/apps/web/src/components/cloud-agent-next/PreparationRow.tsx index a4fcad6294..8e03689971 100644 --- a/apps/web/src/components/cloud-agent-next/PreparationRow.tsx +++ b/apps/web/src/components/cloud-agent-next/PreparationRow.tsx @@ -1,5 +1,5 @@ import { AlertCircle, Check } from 'lucide-react'; -import type { PreparationAttempt } from '@/lib/cloud-agent-sdk'; +import type { PreparationAttempt } from '@kilocode/cloud-agent-sdk'; import { StatusSpinner } from '@/components/shared/StatusSpinner'; import { extractTickerLines, diff --git a/apps/web/src/components/cloud-agent-next/SessionStatusIndicator.tsx b/apps/web/src/components/cloud-agent-next/SessionStatusIndicator.tsx index 4536d4bb33..2fcd2bc292 100644 --- a/apps/web/src/components/cloud-agent-next/SessionStatusIndicator.tsx +++ b/apps/web/src/components/cloud-agent-next/SessionStatusIndicator.tsx @@ -1,5 +1,5 @@ import { AlertCircle, Check } from 'lucide-react'; -import type { SessionStatusIndicator as SessionStatusIndicatorType } from '@/lib/cloud-agent-sdk'; +import type { SessionStatusIndicator as SessionStatusIndicatorType } from '@kilocode/cloud-agent-sdk'; import { StatusSpinner } from '@/components/shared/StatusSpinner'; export function SessionStatusIndicator({ indicator }: { indicator: SessionStatusIndicatorType }) { diff --git a/apps/web/src/components/cloud-agent-next/SuggestionCard.tsx b/apps/web/src/components/cloud-agent-next/SuggestionCard.tsx index c7a0c47e1b..ddfc705006 100644 --- a/apps/web/src/components/cloud-agent-next/SuggestionCard.tsx +++ b/apps/web/src/components/cloud-agent-next/SuggestionCard.tsx @@ -4,7 +4,7 @@ import { useState, useCallback, createContext, useContext, useMemo, type ReactNo import { Sparkles, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; -import type { SuggestionAction } from '@/lib/cloud-agent-sdk'; +import type { SuggestionAction } from '@kilocode/cloud-agent-sdk'; // --------------------------------------------------------------------------- // SuggestionContext — mirrors PermissionContext pattern diff --git a/apps/web/src/components/cloud-agent-next/delivery-badge.ts b/apps/web/src/components/cloud-agent-next/delivery-badge.ts index dbcdd025d0..7470fb7294 100644 --- a/apps/web/src/components/cloud-agent-next/delivery-badge.ts +++ b/apps/web/src/components/cloud-agent-next/delivery-badge.ts @@ -1,4 +1,4 @@ -import type { MessageDeliveryState } from '@/lib/cloud-agent-sdk'; +import type { MessageDeliveryState } from '@kilocode/cloud-agent-sdk'; export type DeliveryBadge = { label: 'Queued' | 'Failed to deliver'; diff --git a/apps/web/src/components/cloud-agent-next/hooks/useActiveSessions.ts b/apps/web/src/components/cloud-agent-next/hooks/useActiveSessions.ts index db4ccf8bc7..5e237835b9 100644 --- a/apps/web/src/components/cloud-agent-next/hooks/useActiveSessions.ts +++ b/apps/web/src/components/cloud-agent-next/hooks/useActiveSessions.ts @@ -10,7 +10,7 @@ import { heartbeatDataSchema, sessionsListDataSchema, type ActiveSessionWithConnectionData, -} from '@/lib/cloud-agent-sdk/schemas'; +} from '@kilocode/cloud-agent-sdk/schemas'; import { useUserWebConnection } from '../CloudAgentProvider'; export type ActiveSession = ActiveSessionWithConnectionData; diff --git a/apps/web/src/components/cloud-agent-next/hooks/useSessionModels.test.ts b/apps/web/src/components/cloud-agent-next/hooks/useSessionModels.test.ts index f438521dc5..5347ba5f53 100644 --- a/apps/web/src/components/cloud-agent-next/hooks/useSessionModels.test.ts +++ b/apps/web/src/components/cloud-agent-next/hooks/useSessionModels.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from '@jest/globals'; -import type { RemoteModelState } from '@/lib/cloud-agent-sdk'; +import type { RemoteModelState } from '@kilocode/cloud-agent-sdk'; import type { ModelOption } from '@/components/shared/ModelCombobox'; import { buildSessionModels, diff --git a/apps/web/src/components/cloud-agent-next/hooks/useSessionModels.ts b/apps/web/src/components/cloud-agent-next/hooks/useSessionModels.ts index 26cb19162d..18c87d2d5a 100644 --- a/apps/web/src/components/cloud-agent-next/hooks/useSessionModels.ts +++ b/apps/web/src/components/cloud-agent-next/hooks/useSessionModels.ts @@ -10,7 +10,7 @@ import { type RemoteModelOverride, type RemoteModelState, type ResolvedSession, -} from '@/lib/cloud-agent-sdk'; +} from '@kilocode/cloud-agent-sdk'; import type { ModelOption } from '@/components/shared/ModelCombobox'; import { buildContextLengthByProviderAndModel, diff --git a/apps/web/src/components/cloud-agent-next/hooks/useSidebarSessions.test.ts b/apps/web/src/components/cloud-agent-next/hooks/useSidebarSessions.test.ts index 64034802f7..2f8d4db517 100644 --- a/apps/web/src/components/cloud-agent-next/hooks/useSidebarSessions.test.ts +++ b/apps/web/src/components/cloud-agent-next/hooks/useSidebarSessions.test.ts @@ -1,4 +1,4 @@ -import type { UserWebSessionEventData } from '@/lib/cloud-agent-sdk'; +import type { UserWebSessionEventData } from '@kilocode/cloud-agent-sdk'; import type { DbSessionV2 } from '../store/db-session-atoms'; import { createSidebarQueryReconciler, diff --git a/apps/web/src/components/cloud-agent-next/hooks/useSidebarSessions.ts b/apps/web/src/components/cloud-agent-next/hooks/useSidebarSessions.ts index 9f74769662..53ec88729d 100644 --- a/apps/web/src/components/cloud-agent-next/hooks/useSidebarSessions.ts +++ b/apps/web/src/components/cloud-agent-next/hooks/useSidebarSessions.ts @@ -10,7 +10,7 @@ import { useAtomValue, useSetAtom } from 'jotai'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useTRPC } from '@/lib/trpc/utils'; import { useUserWebConnection } from '../CloudAgentProvider'; -import type { UserWebSessionEventData } from '@/lib/cloud-agent-sdk'; +import type { UserWebSessionEventData } from '@kilocode/cloud-agent-sdk'; import { apiSessionToDbSession, dbSessionsAtom, diff --git a/apps/web/src/components/cloud-agent-next/model-context-lengths.test.ts b/apps/web/src/components/cloud-agent-next/model-context-lengths.test.ts index 31d1ad6b99..ba2067ee95 100644 --- a/apps/web/src/components/cloud-agent-next/model-context-lengths.test.ts +++ b/apps/web/src/components/cloud-agent-next/model-context-lengths.test.ts @@ -1,4 +1,4 @@ -import type { ContextUsage } from '@/lib/cloud-agent-sdk/context-usage'; +import type { ContextUsage } from '@kilocode/cloud-agent-sdk/context-usage'; import { buildContextLengthByModelId, buildContextLengthByProviderAndModel, diff --git a/apps/web/src/components/cloud-agent-next/model-context-lengths.ts b/apps/web/src/components/cloud-agent-next/model-context-lengths.ts index 4456c804ec..b2bf7ab322 100644 --- a/apps/web/src/components/cloud-agent-next/model-context-lengths.ts +++ b/apps/web/src/components/cloud-agent-next/model-context-lengths.ts @@ -1,4 +1,4 @@ -import type { ContextUsage } from '@/lib/cloud-agent-sdk/context-usage'; +import type { ContextUsage } from '@kilocode/cloud-agent-sdk/context-usage'; type ModelContextLength = { id: string; diff --git a/apps/web/src/components/cloud-agent-next/preparation-phases.test.ts b/apps/web/src/components/cloud-agent-next/preparation-phases.test.ts index 79a2fc58af..b0206d8f3e 100644 --- a/apps/web/src/components/cloud-agent-next/preparation-phases.test.ts +++ b/apps/web/src/components/cloud-agent-next/preparation-phases.test.ts @@ -1,4 +1,4 @@ -import type { PreparationStepSnapshot } from '@/lib/cloud-agent-sdk'; +import type { PreparationStepSnapshot } from '@kilocode/cloud-agent-sdk'; import { formatAttemptDuration, humanizePhaseLabel, diff --git a/apps/web/src/components/cloud-agent-next/preparation-phases.ts b/apps/web/src/components/cloud-agent-next/preparation-phases.ts index 2f7f5ed1db..11ec7714fb 100644 --- a/apps/web/src/components/cloud-agent-next/preparation-phases.ts +++ b/apps/web/src/components/cloud-agent-next/preparation-phases.ts @@ -2,7 +2,7 @@ import type { PreparationAttempt, PreparationStepSnapshot, PreparationStepStatus, -} from '@/lib/cloud-agent-sdk'; +} from '@kilocode/cloud-agent-sdk'; export function humanizePhaseLabel(step: Pick): string { return step.label diff --git a/apps/web/src/components/cloud-agent-next/preparation-summary.test.ts b/apps/web/src/components/cloud-agent-next/preparation-summary.test.ts index f8240321dd..798b417194 100644 --- a/apps/web/src/components/cloud-agent-next/preparation-summary.test.ts +++ b/apps/web/src/components/cloud-agent-next/preparation-summary.test.ts @@ -1,4 +1,4 @@ -import type { PreparationAttempt, PreparationStepSnapshot } from '@/lib/cloud-agent-sdk'; +import type { PreparationAttempt, PreparationStepSnapshot } from '@kilocode/cloud-agent-sdk'; import { extractTickerLines, findRunningSetupCommand, diff --git a/apps/web/src/components/cloud-agent-next/preparation-summary.ts b/apps/web/src/components/cloud-agent-next/preparation-summary.ts index a6d84bded7..040b689587 100644 --- a/apps/web/src/components/cloud-agent-next/preparation-summary.ts +++ b/apps/web/src/components/cloud-agent-next/preparation-summary.ts @@ -1,7 +1,7 @@ -import type { PreparationAttempt, PreparationStepSnapshot } from '@/lib/cloud-agent-sdk'; +import type { PreparationAttempt, PreparationStepSnapshot } from '@kilocode/cloud-agent-sdk'; import { formatAttemptDuration, humanizePhaseLabel } from './preparation-phases'; -export { isNoOpCompletedPreparationAttempt } from '@/lib/cloud-agent-sdk'; +export { isNoOpCompletedPreparationAttempt } from '@kilocode/cloud-agent-sdk'; /** The single line the chat row shows for a preparation attempt. */ export type PreparationRowSummary = diff --git a/apps/web/src/hooks/slash-command-selection.ts b/apps/web/src/hooks/slash-command-selection.ts index 8b2ac1c45e..9996bee88c 100644 --- a/apps/web/src/hooks/slash-command-selection.ts +++ b/apps/web/src/hooks/slash-command-selection.ts @@ -1,6 +1,6 @@ import { commandsOrDefault } from '@cloud-agent-shared'; -import type { SlashCommandInfo } from '@/lib/cloud-agent-sdk'; -import type { ActiveSessionType } from '@/lib/cloud-agent-sdk'; +import type { SlashCommandInfo } from '@kilocode/cloud-agent-sdk'; +import type { ActiveSessionType } from '@kilocode/cloud-agent-sdk'; import type { SlashCommand } from '@/lib/cloud-agent/slash-commands'; /** diff --git a/apps/web/src/hooks/useSlashCommandSets.test.ts b/apps/web/src/hooks/useSlashCommandSets.test.ts index ad3cbff8b1..50439a90a9 100644 --- a/apps/web/src/hooks/useSlashCommandSets.test.ts +++ b/apps/web/src/hooks/useSlashCommandSets.test.ts @@ -1,7 +1,7 @@ -import type { SlashCommandInfo } from '@/lib/cloud-agent-sdk'; -import type { ActiveSessionType } from '@/lib/cloud-agent-sdk'; +import type { SlashCommandInfo } from '@kilocode/cloud-agent-sdk'; +import type { ActiveSessionType } from '@kilocode/cloud-agent-sdk'; import { atom, createStore } from 'jotai'; -import type { SessionManager } from '@/lib/cloud-agent-sdk'; +import type { SessionManager } from '@kilocode/cloud-agent-sdk'; import { selectSlashCommands } from './slash-command-selection'; import { useSlashCommandSets } from './useSlashCommandSets'; diff --git a/apps/web/src/lib/cloud-agent-next/event-types.ts b/apps/web/src/lib/cloud-agent-next/event-types.ts index eaf864738a..5350ba0936 100644 --- a/apps/web/src/lib/cloud-agent-next/event-types.ts +++ b/apps/web/src/lib/cloud-agent-next/event-types.ts @@ -1,92 +1 @@ -/** - * Cloud Agent Event Types - * - * Type definitions and validation for WebSocket stream events. - * Events follow the OpenNext message format. - */ - -/** - * Event received from the cloud agent WebSocket stream. - */ -export type CloudAgentEvent = { - eventId: number; - executionId?: string | null; - sessionId: string; - streamEventType: string; - timestamp: string; - data: unknown; -}; - -/** - * Error codes for WebSocket protocol errors. - * These match the WS_* codes from cloud-agent-next/src/websocket/types.ts. - */ -export type StreamErrorCode = - | 'WS_PROTOCOL_ERROR' - | 'WS_AUTH_ERROR' - | 'WS_SESSION_NOT_FOUND' - | 'WS_EXECUTION_NOT_FOUND' - | 'WS_DUPLICATE_CONNECTION' - | 'WS_INTERNAL_ERROR'; - -const STREAM_ERROR_CODES: ReadonlySet = new Set([ - 'WS_PROTOCOL_ERROR', - 'WS_AUTH_ERROR', - 'WS_SESSION_NOT_FOUND', - 'WS_EXECUTION_NOT_FOUND', - 'WS_DUPLICATE_CONNECTION', - 'WS_INTERNAL_ERROR', -]); - -/** - * Error sent over the WebSocket stream. - */ -export type StreamError = { - type: 'error'; - code: StreamErrorCode; - message: string; -}; - -/** - * Type guard to validate a CloudAgentEvent from unknown data. - */ -export function isValidCloudAgentEvent(event: unknown): event is CloudAgentEvent { - if (typeof event !== 'object' || event === null) { - return false; - } - const hasValidExecutionId = - !('executionId' in event) || - event.executionId === undefined || - event.executionId === null || - typeof event.executionId === 'string'; - return ( - 'eventId' in event && - typeof event.eventId === 'number' && - hasValidExecutionId && - 'sessionId' in event && - typeof event.sessionId === 'string' && - 'streamEventType' in event && - typeof event.streamEventType === 'string' && - 'timestamp' in event && - typeof event.timestamp === 'string' && - 'data' in event - ); -} - -/** - * Type guard to validate a StreamError from unknown data. - */ -export function isStreamError(data: unknown): data is StreamError { - if (typeof data !== 'object' || data === null) { - return false; - } - return ( - 'type' in data && - data.type === 'error' && - 'code' in data && - typeof data.code === 'string' && - STREAM_ERROR_CODES.has(data.code) && - 'message' in data && - typeof data.message === 'string' - ); -} +export * from '@kilocode/cloud-agent-sdk'; diff --git a/apps/web/src/lib/cloud-agent/constants.ts b/apps/web/src/lib/cloud-agent/constants.ts index 69896be5df..59234cf409 100644 --- a/apps/web/src/lib/cloud-agent/constants.ts +++ b/apps/web/src/lib/cloud-agent/constants.ts @@ -175,10 +175,7 @@ export function normalizeAttachmentExtension(extension: string | undefined | nul return CLOUD_AGENT_ATTACHMENT_FALLBACK_EXTENSION; } -export type CloudAgentAttachments = { - path: string; - files: string[]; -}; +export type { CloudAgentAttachments } from '@kilocode/app-shared/cloud-agent'; /** * Maximum prompt length (in characters) accepted by the cloud agent. diff --git a/apps/web/src/lib/images-schema.ts b/apps/web/src/lib/images-schema.ts index 8f3688f56c..6439473d2e 100644 --- a/apps/web/src/lib/images-schema.ts +++ b/apps/web/src/lib/images-schema.ts @@ -1,35 +1 @@ -/** - * Generic schema and type for image attachments. - * - * This file is intentionally isolated to avoid circular dependencies. - * It can be imported from both cloud-agent and app-builder modules. - * - * R2 path structure: {bucket}/{userId}/{path}/{filename} - * - userId is derived from the authenticated user context - * - path is app-specific (e.g., "app-builder/msg-uuid", "cloud-agent/session123") - * - files are either specified explicitly or all files at the path are downloaded - */ -import * as z from 'zod'; - -/** - * Generic images schema for attaching images to prompts. - */ -export const imagesSchema = z - .object({ - /** - * App-specific path within the user's namespace. - * Examples: "app-builder/msg-uuid-123", "cloud-agent/session-456" - */ - path: z.string().min(1).max(500), - /** - * Ordered list of filenames to download. - */ - files: z.array(z.string().max(255)).min(1), - }) - .optional(); - -/** - * Image attachments with path and optional ordered list of filenames. - * Derived from imagesSchema zod validator. - */ -export type Images = NonNullable>; +export * from '@kilocode/app-shared/images-schema'; diff --git a/apps/web/src/routers/cloud-agent-next-router.ts b/apps/web/src/routers/cloud-agent-next-router.ts index 8c87dd9853..9ebb2839b9 100644 --- a/apps/web/src/routers/cloud-agent-next-router.ts +++ b/apps/web/src/routers/cloud-agent-next-router.ts @@ -50,7 +50,7 @@ import { signStreamTicket } from '@/lib/cloud-agent/stream-ticket'; import { db } from '@/lib/drizzle'; import { verifyUserOwnsSessionV2ByCloudAgentId } from '@/lib/cloud-agent/session-ownership'; import { TRPCError } from '@trpc/server'; -import { generateMessageId } from '@/lib/cloud-agent-sdk/message-id'; +import { generateMessageId } from '@kilocode/cloud-agent-sdk/message-id'; import { getBalanceForUser } from '@/lib/user/balance'; import { buildCloudAgentNextEligibility } from './cloud-agent-next-eligibility'; diff --git a/apps/web/src/routers/organizations/organization-cloud-agent-next-router.ts b/apps/web/src/routers/organizations/organization-cloud-agent-next-router.ts index 8d70a21dcf..326292d205 100644 --- a/apps/web/src/routers/organizations/organization-cloud-agent-next-router.ts +++ b/apps/web/src/routers/organizations/organization-cloud-agent-next-router.ts @@ -56,7 +56,7 @@ import { signStreamTicket } from '@/lib/cloud-agent/stream-ticket'; import { db } from '@/lib/drizzle'; import { verifyOrgOwnsSessionV2ByCloudAgentId } from '@/lib/cloud-agent/session-ownership'; import { TRPCError } from '@trpc/server'; -import { generateMessageId } from '@/lib/cloud-agent-sdk/message-id'; +import { generateMessageId } from '@kilocode/cloud-agent-sdk/message-id'; import { getBalanceForOrganizationUser } from '@/lib/organizations/organization-usage'; import { buildCloudAgentNextEligibility } from '../cloud-agent-next-eligibility'; diff --git a/apps/web/src/types/opencode.gen.ts b/apps/web/src/types/opencode.gen.ts index da8fb83dd1..69e3c9d1fa 100644 --- a/apps/web/src/types/opencode.gen.ts +++ b/apps/web/src/types/opencode.gen.ts @@ -1,622 +1 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type OutputFormatText = { - type: 'text'; -}; - -export type JsonSchema = { - [key: string]: unknown; -}; - -export type OutputFormatJsonSchema = { - type: 'json_schema'; - schema: JsonSchema; - retryCount?: number; -}; - -export type OutputFormat = OutputFormatText | OutputFormatJsonSchema; - -export type FileDiff = { - file: string; - before: string; - after: string; - additions: number; - deletions: number; - status?: 'added' | 'deleted' | 'modified'; -}; - -export type UserMessage = { - id: string; - sessionID: string; - role: 'user'; - time: { - created: number; - }; - format?: OutputFormat; - summary?: { - title?: string; - body?: string; - diffs: Array; - }; - agent: string; - model: { - providerID: string; - modelID: string; - }; - system?: string; - tools?: { - [key: string]: boolean; - }; - variant?: string; - editorContext?: { - visibleFiles?: Array; - openTabs?: Array; - activeFile?: string; - shell?: string; - timezone?: string; - }; -}; - -export type ProviderAuthError = { - name: 'ProviderAuthError'; - data: { - providerID: string; - message: string; - }; -}; - -export type UnknownError = { - name: 'UnknownError'; - data: { - message: string; - }; -}; - -export type MessageOutputLengthError = { - name: 'MessageOutputLengthError'; - data: { - [key: string]: unknown; - }; -}; - -export type MessageAbortedError = { - name: 'MessageAbortedError'; - data: { - message: string; - }; -}; - -export type StructuredOutputError = { - name: 'StructuredOutputError'; - data: { - message: string; - retries: number; - }; -}; - -export type ContextOverflowError = { - name: 'ContextOverflowError'; - data: { - message: string; - responseBody?: string; - }; -}; - -export type ApiError = { - name: 'APIError'; - data: { - message: string; - statusCode?: number; - isRetryable: boolean; - responseHeaders?: { - [key: string]: string; - }; - responseBody?: string; - metadata?: { - [key: string]: string; - }; - }; -}; - -export type AssistantMessage = { - id: string; - sessionID: string; - role: 'assistant'; - time: { - created: number; - completed?: number; - }; - error?: - | ProviderAuthError - | UnknownError - | MessageOutputLengthError - | MessageAbortedError - | StructuredOutputError - | ContextOverflowError - | ApiError; - parentID: string; - modelID: string; - providerID: string; - mode: string; - agent: string; - path: { - cwd: string; - root: string; - }; - summary?: boolean; - cost: number; - tokens: { - total?: number; - input: number; - output: number; - reasoning: number; - cache: { - read: number; - write: number; - }; - }; - structured?: unknown; - variant?: string; - finish?: string; -}; - -export type Message = UserMessage | AssistantMessage; - -export type EventMessageUpdated = { - id: string; - type: 'message.updated'; - properties: { - sessionID: string; - info: Message; - }; -}; - -export type TextPart = { - id: string; - sessionID: string; - messageID: string; - type: 'text'; - text: string; - synthetic?: boolean; - ignored?: boolean; - time?: { - start: number; - end?: number; - }; - metadata?: { - [key: string]: unknown; - }; -}; - -export type SubtaskPart = { - id: string; - sessionID: string; - messageID: string; - type: 'subtask'; - prompt: string; - description: string; - agent: string; - model?: { - providerID: string; - modelID: string; - }; - command?: string; -}; - -export type ReasoningPart = { - id: string; - sessionID: string; - messageID: string; - type: 'reasoning'; - text: string; - metadata?: { - [key: string]: unknown; - }; - time: { - start: number; - end?: number; - }; -}; - -export type FilePartSourceText = { - value: string; - start: number; - end: number; -}; - -export type FileSource = { - text: FilePartSourceText; - type: 'file'; - path: string; -}; - -export type Range = { - start: { - line: number; - character: number; - }; - end: { - line: number; - character: number; - }; -}; - -export type SymbolSource = { - text: FilePartSourceText; - type: 'symbol'; - path: string; - range: Range; - name: string; - kind: number; -}; - -export type ResourceSource = { - text: FilePartSourceText; - type: 'resource'; - clientName: string; - uri: string; -}; - -export type FilePartSource = FileSource | SymbolSource | ResourceSource; - -export type FilePart = { - id: string; - sessionID: string; - messageID: string; - type: 'file'; - mime: string; - filename?: string; - url: string; - source?: FilePartSource; -}; - -export type ToolStatePending = { - status: 'pending'; - input: { - [key: string]: unknown; - }; - raw: string; -}; - -export type ToolStateRunning = { - status: 'running'; - input: { - [key: string]: unknown; - }; - title?: string; - metadata?: { - [key: string]: unknown; - }; - time: { - start: number; - }; -}; - -export type ToolStateCompleted = { - status: 'completed'; - input: { - [key: string]: unknown; - }; - output: string; - title: string; - metadata: { - [key: string]: unknown; - }; - time: { - start: number; - end: number; - compacted?: number; - }; - attachments?: Array; -}; - -export type ToolStateError = { - status: 'error'; - input: { - [key: string]: unknown; - }; - error: string; - metadata?: { - [key: string]: unknown; - }; - time: { - start: number; - end: number; - }; -}; - -export type ToolState = ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError; - -export type ToolPart = { - id: string; - sessionID: string; - messageID: string; - type: 'tool'; - callID: string; - tool: string; - state: ToolState; - metadata?: { - [key: string]: unknown; - }; -}; - -export type StepStartPart = { - id: string; - sessionID: string; - messageID: string; - type: 'step-start'; - snapshot?: string; -}; - -export type StepFinishPart = { - id: string; - sessionID: string; - messageID: string; - type: 'step-finish'; - reason: string; - snapshot?: string; - cost: number; - tokens: { - total?: number; - input: number; - output: number; - reasoning: number; - cache: { - read: number; - write: number; - }; - }; -}; - -export type SnapshotPart = { - id: string; - sessionID: string; - messageID: string; - type: 'snapshot'; - snapshot: string; -}; - -export type PatchPart = { - id: string; - sessionID: string; - messageID: string; - type: 'patch'; - hash: string; - files: Array; -}; - -export type AgentPart = { - id: string; - sessionID: string; - messageID: string; - type: 'agent'; - name: string; - source?: { - value: string; - start: number; - end: number; - }; -}; - -export type RetryPart = { - id: string; - sessionID: string; - messageID: string; - type: 'retry'; - attempt: number; - error: ApiError; - time: { - created: number; - }; -}; - -export type CompactionPart = { - id: string; - sessionID: string; - messageID: string; - type: 'compaction'; - auto: boolean; - overflow?: boolean; - tail_start_id?: string; -}; - -export type Part = - | TextPart - | SubtaskPart - | ReasoningPart - | FilePart - | ToolPart - | StepStartPart - | StepFinishPart - | SnapshotPart - | PatchPart - | AgentPart - | RetryPart - | CompactionPart; - -export type EventMessagePartUpdated = { - id: string; - type: 'message.part.updated'; - properties: { - sessionID: string; - part: Part; - time: number; - }; -}; - -export type EventMessagePartDelta = { - id: string; - type: 'message.part.delta'; - properties: { - sessionID: string; - messageID: string; - partID: string; - field: string; - delta: string; - }; -}; - -export type EventMessagePartRemoved = { - id: string; - type: 'message.part.removed'; - properties: { - sessionID: string; - messageID: string; - partID: string; - }; -}; - -export type SessionStatus = - | { - type: 'idle'; - } - | { - type: 'retry'; - attempt: number; - message: string; - action?: { - reason: string; - provider: string; - title: string; - message: string; - label: string; - link?: string; - }; - next: number; - } - | { - type: 'busy'; - } - | { - type: 'offline'; - requestID: string; - message: string; - }; - -export type EventSessionStatus = { - id: string; - type: 'session.status'; - properties: { - sessionID: string; - status: SessionStatus; - }; -}; - -export type QuestionOption = { - /** - * Display text (1-5 words, concise) - */ - label: string; - /** - * Explanation of choice - */ - description: string; - /** - * Optional agent/mode to switch to when selected (e.g. code, debug, orchestrator) - */ - mode?: string; -}; - -export type QuestionInfo = { - /** - * Complete question - */ - question: string; - /** - * Very short label (max 30 chars) - */ - header: string; - /** - * Available choices - */ - options: Array; - /** - * Allow selecting multiple choices - */ - multiple?: boolean; - /** - * Allow typing a custom answer (default: true) - */ - custom?: boolean; -}; - -export type PermissionAction = 'allow' | 'deny' | 'ask'; - -export type PermissionRule = { - permission: string; - pattern: string; - action: PermissionAction; -}; - -export type PermissionRuleset = Array; - -export type Session = { - id: string; - slug: string; - projectID: string; - workspaceID?: string; - directory: string; - path?: string; - parentID?: string; - summary?: { - additions: number; - deletions: number; - files: number; - diffs?: Array; - }; - cost?: number; - tokens?: { - input: number; - output: number; - reasoning: number; - cache: { - read: number; - write: number; - }; - }; - share?: { - url: string; - }; - title: string; - agent?: string; - model?: { - id: string; - providerID: string; - variant?: string; - }; - version: string; - metadata?: { - [key: string]: unknown; - }; - time: { - created: number; - updated: number; - compacting?: number; - archived?: number; - }; - permission?: PermissionRuleset; - revert?: { - messageID: string; - partID?: string; - snapshot?: string; - diff?: string; - }; -}; - -export type EventSessionCreated = { - id: string; - type: 'session.created'; - properties: { - sessionID: string; - info: Session; - }; -}; - -export type EventSessionUpdated = { - id: string; - type: 'session.updated'; - properties: { - sessionID: string; - info: Session; - }; -}; +export * from '@kilocode/app-shared/opencode'; diff --git a/packages/app-shared/package.json b/packages/app-shared/package.json index 7182025696..14a4a5712f 100644 --- a/packages/app-shared/package.json +++ b/packages/app-shared/package.json @@ -9,13 +9,19 @@ "./security-agent": "./src/security-agent/index.ts", "./code-review": "./src/code-review/index.ts", "./organizations": "./src/organizations/index.ts", - "./platforms": "./src/platforms.ts" + "./platforms": "./src/platforms.ts", + "./opencode": "./src/opencode.gen.ts", + "./images-schema": "./src/images-schema.ts", + "./cloud-agent": "./src/cloud-agent.ts" }, "scripts": { "typecheck": "tsgo --noEmit", "test": "vitest run", "test:watch": "vitest" }, + "dependencies": { + "zod": "catalog:" + }, "devDependencies": { "@typescript/native-preview": "catalog:", "typescript": "catalog:", diff --git a/packages/app-shared/src/cloud-agent.ts b/packages/app-shared/src/cloud-agent.ts new file mode 100644 index 0000000000..8e5905d2b2 --- /dev/null +++ b/packages/app-shared/src/cloud-agent.ts @@ -0,0 +1,9 @@ +/** + * Cloud agent attachment reference shared by the web and mobile apps. + * + * R2 path structure: {bucket}/{userId}/{path}/{filename} + */ +export type CloudAgentAttachments = { + path: string; + files: string[]; +}; diff --git a/packages/app-shared/src/images-schema.ts b/packages/app-shared/src/images-schema.ts new file mode 100644 index 0000000000..8f3688f56c --- /dev/null +++ b/packages/app-shared/src/images-schema.ts @@ -0,0 +1,35 @@ +/** + * Generic schema and type for image attachments. + * + * This file is intentionally isolated to avoid circular dependencies. + * It can be imported from both cloud-agent and app-builder modules. + * + * R2 path structure: {bucket}/{userId}/{path}/{filename} + * - userId is derived from the authenticated user context + * - path is app-specific (e.g., "app-builder/msg-uuid", "cloud-agent/session123") + * - files are either specified explicitly or all files at the path are downloaded + */ +import * as z from 'zod'; + +/** + * Generic images schema for attaching images to prompts. + */ +export const imagesSchema = z + .object({ + /** + * App-specific path within the user's namespace. + * Examples: "app-builder/msg-uuid-123", "cloud-agent/session-456" + */ + path: z.string().min(1).max(500), + /** + * Ordered list of filenames to download. + */ + files: z.array(z.string().max(255)).min(1), + }) + .optional(); + +/** + * Image attachments with path and optional ordered list of filenames. + * Derived from imagesSchema zod validator. + */ +export type Images = NonNullable>; diff --git a/packages/app-shared/src/opencode.gen.ts b/packages/app-shared/src/opencode.gen.ts new file mode 100644 index 0000000000..38f85f1735 --- /dev/null +++ b/packages/app-shared/src/opencode.gen.ts @@ -0,0 +1,623 @@ +// This file is auto-generated by @hey-api/openapi-ts +// NOTE: the generator output moved here from apps/web/src/types/opencode.gen.ts. + +export type OutputFormatText = { + type: 'text'; +}; + +export type JsonSchema = { + [key: string]: unknown; +}; + +export type OutputFormatJsonSchema = { + type: 'json_schema'; + schema: JsonSchema; + retryCount?: number; +}; + +export type OutputFormat = OutputFormatText | OutputFormatJsonSchema; + +export type FileDiff = { + file: string; + before: string; + after: string; + additions: number; + deletions: number; + status?: 'added' | 'deleted' | 'modified'; +}; + +export type UserMessage = { + id: string; + sessionID: string; + role: 'user'; + time: { + created: number; + }; + format?: OutputFormat; + summary?: { + title?: string; + body?: string; + diffs: Array; + }; + agent: string; + model: { + providerID: string; + modelID: string; + }; + system?: string; + tools?: { + [key: string]: boolean; + }; + variant?: string; + editorContext?: { + visibleFiles?: Array; + openTabs?: Array; + activeFile?: string; + shell?: string; + timezone?: string; + }; +}; + +export type ProviderAuthError = { + name: 'ProviderAuthError'; + data: { + providerID: string; + message: string; + }; +}; + +export type UnknownError = { + name: 'UnknownError'; + data: { + message: string; + }; +}; + +export type MessageOutputLengthError = { + name: 'MessageOutputLengthError'; + data: { + [key: string]: unknown; + }; +}; + +export type MessageAbortedError = { + name: 'MessageAbortedError'; + data: { + message: string; + }; +}; + +export type StructuredOutputError = { + name: 'StructuredOutputError'; + data: { + message: string; + retries: number; + }; +}; + +export type ContextOverflowError = { + name: 'ContextOverflowError'; + data: { + message: string; + responseBody?: string; + }; +}; + +export type ApiError = { + name: 'APIError'; + data: { + message: string; + statusCode?: number; + isRetryable: boolean; + responseHeaders?: { + [key: string]: string; + }; + responseBody?: string; + metadata?: { + [key: string]: string; + }; + }; +}; + +export type AssistantMessage = { + id: string; + sessionID: string; + role: 'assistant'; + time: { + created: number; + completed?: number; + }; + error?: + | ProviderAuthError + | UnknownError + | MessageOutputLengthError + | MessageAbortedError + | StructuredOutputError + | ContextOverflowError + | ApiError; + parentID: string; + modelID: string; + providerID: string; + mode: string; + agent: string; + path: { + cwd: string; + root: string; + }; + summary?: boolean; + cost: number; + tokens: { + total?: number; + input: number; + output: number; + reasoning: number; + cache: { + read: number; + write: number; + }; + }; + structured?: unknown; + variant?: string; + finish?: string; +}; + +export type Message = UserMessage | AssistantMessage; + +export type EventMessageUpdated = { + id: string; + type: 'message.updated'; + properties: { + sessionID: string; + info: Message; + }; +}; + +export type TextPart = { + id: string; + sessionID: string; + messageID: string; + type: 'text'; + text: string; + synthetic?: boolean; + ignored?: boolean; + time?: { + start: number; + end?: number; + }; + metadata?: { + [key: string]: unknown; + }; +}; + +export type SubtaskPart = { + id: string; + sessionID: string; + messageID: string; + type: 'subtask'; + prompt: string; + description: string; + agent: string; + model?: { + providerID: string; + modelID: string; + }; + command?: string; +}; + +export type ReasoningPart = { + id: string; + sessionID: string; + messageID: string; + type: 'reasoning'; + text: string; + metadata?: { + [key: string]: unknown; + }; + time: { + start: number; + end?: number; + }; +}; + +export type FilePartSourceText = { + value: string; + start: number; + end: number; +}; + +export type FileSource = { + text: FilePartSourceText; + type: 'file'; + path: string; +}; + +export type Range = { + start: { + line: number; + character: number; + }; + end: { + line: number; + character: number; + }; +}; + +export type SymbolSource = { + text: FilePartSourceText; + type: 'symbol'; + path: string; + range: Range; + name: string; + kind: number; +}; + +export type ResourceSource = { + text: FilePartSourceText; + type: 'resource'; + clientName: string; + uri: string; +}; + +export type FilePartSource = FileSource | SymbolSource | ResourceSource; + +export type FilePart = { + id: string; + sessionID: string; + messageID: string; + type: 'file'; + mime: string; + filename?: string; + url: string; + source?: FilePartSource; +}; + +export type ToolStatePending = { + status: 'pending'; + input: { + [key: string]: unknown; + }; + raw: string; +}; + +export type ToolStateRunning = { + status: 'running'; + input: { + [key: string]: unknown; + }; + title?: string; + metadata?: { + [key: string]: unknown; + }; + time: { + start: number; + }; +}; + +export type ToolStateCompleted = { + status: 'completed'; + input: { + [key: string]: unknown; + }; + output: string; + title: string; + metadata: { + [key: string]: unknown; + }; + time: { + start: number; + end: number; + compacted?: number; + }; + attachments?: Array; +}; + +export type ToolStateError = { + status: 'error'; + input: { + [key: string]: unknown; + }; + error: string; + metadata?: { + [key: string]: unknown; + }; + time: { + start: number; + end: number; + }; +}; + +export type ToolState = ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError; + +export type ToolPart = { + id: string; + sessionID: string; + messageID: string; + type: 'tool'; + callID: string; + tool: string; + state: ToolState; + metadata?: { + [key: string]: unknown; + }; +}; + +export type StepStartPart = { + id: string; + sessionID: string; + messageID: string; + type: 'step-start'; + snapshot?: string; +}; + +export type StepFinishPart = { + id: string; + sessionID: string; + messageID: string; + type: 'step-finish'; + reason: string; + snapshot?: string; + cost: number; + tokens: { + total?: number; + input: number; + output: number; + reasoning: number; + cache: { + read: number; + write: number; + }; + }; +}; + +export type SnapshotPart = { + id: string; + sessionID: string; + messageID: string; + type: 'snapshot'; + snapshot: string; +}; + +export type PatchPart = { + id: string; + sessionID: string; + messageID: string; + type: 'patch'; + hash: string; + files: Array; +}; + +export type AgentPart = { + id: string; + sessionID: string; + messageID: string; + type: 'agent'; + name: string; + source?: { + value: string; + start: number; + end: number; + }; +}; + +export type RetryPart = { + id: string; + sessionID: string; + messageID: string; + type: 'retry'; + attempt: number; + error: ApiError; + time: { + created: number; + }; +}; + +export type CompactionPart = { + id: string; + sessionID: string; + messageID: string; + type: 'compaction'; + auto: boolean; + overflow?: boolean; + tail_start_id?: string; +}; + +export type Part = + | TextPart + | SubtaskPart + | ReasoningPart + | FilePart + | ToolPart + | StepStartPart + | StepFinishPart + | SnapshotPart + | PatchPart + | AgentPart + | RetryPart + | CompactionPart; + +export type EventMessagePartUpdated = { + id: string; + type: 'message.part.updated'; + properties: { + sessionID: string; + part: Part; + time: number; + }; +}; + +export type EventMessagePartDelta = { + id: string; + type: 'message.part.delta'; + properties: { + sessionID: string; + messageID: string; + partID: string; + field: string; + delta: string; + }; +}; + +export type EventMessagePartRemoved = { + id: string; + type: 'message.part.removed'; + properties: { + sessionID: string; + messageID: string; + partID: string; + }; +}; + +export type SessionStatus = + | { + type: 'idle'; + } + | { + type: 'retry'; + attempt: number; + message: string; + action?: { + reason: string; + provider: string; + title: string; + message: string; + label: string; + link?: string; + }; + next: number; + } + | { + type: 'busy'; + } + | { + type: 'offline'; + requestID: string; + message: string; + }; + +export type EventSessionStatus = { + id: string; + type: 'session.status'; + properties: { + sessionID: string; + status: SessionStatus; + }; +}; + +export type QuestionOption = { + /** + * Display text (1-5 words, concise) + */ + label: string; + /** + * Explanation of choice + */ + description: string; + /** + * Optional agent/mode to switch to when selected (e.g. code, debug, orchestrator) + */ + mode?: string; +}; + +export type QuestionInfo = { + /** + * Complete question + */ + question: string; + /** + * Very short label (max 30 chars) + */ + header: string; + /** + * Available choices + */ + options: Array; + /** + * Allow selecting multiple choices + */ + multiple?: boolean; + /** + * Allow typing a custom answer (default: true) + */ + custom?: boolean; +}; + +export type PermissionAction = 'allow' | 'deny' | 'ask'; + +export type PermissionRule = { + permission: string; + pattern: string; + action: PermissionAction; +}; + +export type PermissionRuleset = Array; + +export type Session = { + id: string; + slug: string; + projectID: string; + workspaceID?: string; + directory: string; + path?: string; + parentID?: string; + summary?: { + additions: number; + deletions: number; + files: number; + diffs?: Array; + }; + cost?: number; + tokens?: { + input: number; + output: number; + reasoning: number; + cache: { + read: number; + write: number; + }; + }; + share?: { + url: string; + }; + title: string; + agent?: string; + model?: { + id: string; + providerID: string; + variant?: string; + }; + version: string; + metadata?: { + [key: string]: unknown; + }; + time: { + created: number; + updated: number; + compacting?: number; + archived?: number; + }; + permission?: PermissionRuleset; + revert?: { + messageID: string; + partID?: string; + snapshot?: string; + diff?: string; + }; +}; + +export type EventSessionCreated = { + id: string; + type: 'session.created'; + properties: { + sessionID: string; + info: Session; + }; +}; + +export type EventSessionUpdated = { + id: string; + type: 'session.updated'; + properties: { + sessionID: string; + info: Session; + }; +}; diff --git a/packages/cloud-agent-sdk/jest.config.mjs b/packages/cloud-agent-sdk/jest.config.mjs new file mode 100644 index 0000000000..7e53bb2be0 --- /dev/null +++ b/packages/cloud-agent-sdk/jest.config.mjs @@ -0,0 +1,17 @@ +/** @type {import('jest').Config} */ +export default { + testEnvironment: 'node', + transform: { + '^.+\\.(t|j)sx?$': [ + '@swc/jest', + { + jsc: { + parser: { + syntax: 'typescript', + }, + }, + }, + ], + }, + testMatch: ['/src/**/*.test.ts'], +}; diff --git a/packages/cloud-agent-sdk/package.json b/packages/cloud-agent-sdk/package.json new file mode 100644 index 0000000000..2216322a44 --- /dev/null +++ b/packages/cloud-agent-sdk/package.json @@ -0,0 +1,41 @@ +{ + "name": "@kilocode/cloud-agent-sdk", + "version": "0.1.0", + "private": true, + "description": "Client-safe cloud agent session SDK shared by the web and mobile apps", + "type": "module", + "exports": { + ".": "./src/index.ts", + "./context-usage": "./src/context-usage.ts", + "./create-session": "./src/create-session.ts", + "./message-id": "./src/message-id.ts", + "./preparation-attempts": "./src/preparation-attempts.ts", + "./remote-command-catalog": "./src/remote-command-catalog.ts", + "./remote-model-order": "./src/remote-model-order.ts", + "./schemas": "./src/schemas.ts", + "./session": "./src/session.ts", + "./user-web-connection": "./src/user-web-connection.ts", + "./*": "./src/*.ts" + }, + "scripts": { + "typecheck": "tsgo --noEmit", + "test": "jest", + "test:watch": "jest --watch" + }, + "dependencies": { + "@kilocode/app-shared": "workspace:*", + "jotai": "2.18.1", + "zod": "catalog:" + }, + "devDependencies": { + "@jest/globals": "30.3.0", + "@kilocode/session-ingest-contracts": "workspace:*", + "@swc/jest": "0.2.39", + "@types/jest": "30.0.0", + "@typescript/native-preview": "catalog:", + "jest": "30.3.0", + "typescript": "catalog:" + }, + "author": "Kilo Code", + "license": "MIT" +} diff --git a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/autocommit.ts b/packages/cloud-agent-sdk/src/__fixtures__/autocommit.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/__fixtures__/autocommit.ts rename to packages/cloud-agent-sdk/src/__fixtures__/autocommit.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/basic-streaming.ts b/packages/cloud-agent-sdk/src/__fixtures__/basic-streaming.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/__fixtures__/basic-streaming.ts rename to packages/cloud-agent-sdk/src/__fixtures__/basic-streaming.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/child-sessions.ts b/packages/cloud-agent-sdk/src/__fixtures__/child-sessions.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/__fixtures__/child-sessions.ts rename to packages/cloud-agent-sdk/src/__fixtures__/child-sessions.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/helpers.ts b/packages/cloud-agent-sdk/src/__fixtures__/helpers.ts similarity index 96% rename from apps/web/src/lib/cloud-agent-sdk/__fixtures__/helpers.ts rename to packages/cloud-agent-sdk/src/__fixtures__/helpers.ts index d0d374542b..f99c9ea58d 100644 --- a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/helpers.ts +++ b/packages/cloud-agent-sdk/src/__fixtures__/helpers.ts @@ -1,4 +1,4 @@ -import type { CloudAgentEvent } from '@/lib/cloud-agent-next/event-types'; +import type { CloudAgentEvent } from '../event-types'; function createEventHelpers() { let eventCounter = 0; diff --git a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/index.ts b/packages/cloud-agent-sdk/src/__fixtures__/index.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/__fixtures__/index.ts rename to packages/cloud-agent-sdk/src/__fixtures__/index.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/interruption.ts b/packages/cloud-agent-sdk/src/__fixtures__/interruption.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/__fixtures__/interruption.ts rename to packages/cloud-agent-sdk/src/__fixtures__/interruption.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/message-delivery-exhausted.ts b/packages/cloud-agent-sdk/src/__fixtures__/message-delivery-exhausted.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/__fixtures__/message-delivery-exhausted.ts rename to packages/cloud-agent-sdk/src/__fixtures__/message-delivery-exhausted.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/optimistic-user-text-reconciliation.ts b/packages/cloud-agent-sdk/src/__fixtures__/optimistic-user-text-reconciliation.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/__fixtures__/optimistic-user-text-reconciliation.ts rename to packages/cloud-agent-sdk/src/__fixtures__/optimistic-user-text-reconciliation.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/real-session-excerpt.ts b/packages/cloud-agent-sdk/src/__fixtures__/real-session-excerpt.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/__fixtures__/real-session-excerpt.ts rename to packages/cloud-agent-sdk/src/__fixtures__/real-session-excerpt.ts index c511323f07..2363441b1b 100644 --- a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/real-session-excerpt.ts +++ b/packages/cloud-agent-sdk/src/__fixtures__/real-session-excerpt.ts @@ -10,7 +10,7 @@ * * Generated by: scripts/extract-fixture.ts */ -import type { CloudAgentEvent } from '@/lib/cloud-agent-next/event-types'; +import type { CloudAgentEvent } from '../event-types'; import type { Fixture } from './types'; const events: CloudAgentEvent[] = [ diff --git a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/tool-use-cycle.ts b/packages/cloud-agent-sdk/src/__fixtures__/tool-use-cycle.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/__fixtures__/tool-use-cycle.ts rename to packages/cloud-agent-sdk/src/__fixtures__/tool-use-cycle.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/types.ts b/packages/cloud-agent-sdk/src/__fixtures__/types.ts similarity index 84% rename from apps/web/src/lib/cloud-agent-sdk/__fixtures__/types.ts rename to packages/cloud-agent-sdk/src/__fixtures__/types.ts index 122d2f8b23..ffa461e8d2 100644 --- a/apps/web/src/lib/cloud-agent-sdk/__fixtures__/types.ts +++ b/packages/cloud-agent-sdk/src/__fixtures__/types.ts @@ -1,4 +1,4 @@ -import type { CloudAgentEvent } from '@/lib/cloud-agent-next/event-types'; +import type { CloudAgentEvent } from '../event-types'; import type { MessageDeliveryState } from '../types'; type ExpectedPart = { diff --git a/apps/web/src/lib/cloud-agent-sdk/array-utils.ts b/packages/cloud-agent-sdk/src/array-utils.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/array-utils.ts rename to packages/cloud-agent-sdk/src/array-utils.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/base-connection.test.ts b/packages/cloud-agent-sdk/src/base-connection.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/base-connection.test.ts rename to packages/cloud-agent-sdk/src/base-connection.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/base-connection.ts b/packages/cloud-agent-sdk/src/base-connection.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/base-connection.ts rename to packages/cloud-agent-sdk/src/base-connection.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/chat-processor.test.ts b/packages/cloud-agent-sdk/src/chat-processor.test.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/chat-processor.test.ts rename to packages/cloud-agent-sdk/src/chat-processor.test.ts index 18555a789c..489444ae73 100644 --- a/apps/web/src/lib/cloud-agent-sdk/chat-processor.test.ts +++ b/packages/cloud-agent-sdk/src/chat-processor.test.ts @@ -1,4 +1,4 @@ -import type { FilePart, Message, Part, TextPart, UserMessage } from '@/types/opencode.gen'; +import type { FilePart, Message, Part, TextPart, UserMessage } from '@kilocode/app-shared/opencode'; import { createChatProcessor } from './chat-processor'; import type { ChatEvent } from './normalizer'; import { createMemoryStorage } from './storage/memory'; diff --git a/apps/web/src/lib/cloud-agent-sdk/chat-processor.ts b/packages/cloud-agent-sdk/src/chat-processor.ts similarity index 97% rename from apps/web/src/lib/cloud-agent-sdk/chat-processor.ts rename to packages/cloud-agent-sdk/src/chat-processor.ts index f0942161d7..ea3da6beb3 100644 --- a/apps/web/src/lib/cloud-agent-sdk/chat-processor.ts +++ b/packages/cloud-agent-sdk/src/chat-processor.ts @@ -1,7 +1,7 @@ import { stripPartContentIfFile } from './part-utils'; import type { ChatEvent } from './normalizer'; import type { SessionStorage } from './storage/types'; -import type { UserMessage, TextPart } from '@/types/opencode.gen'; +import type { UserMessage, TextPart } from '@kilocode/app-shared/opencode'; type ChatProcessor = { process(event: ChatEvent): void; diff --git a/apps/web/src/lib/cloud-agent-sdk/cli-historical-transport.test.ts b/packages/cloud-agent-sdk/src/cli-historical-transport.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/cli-historical-transport.test.ts rename to packages/cloud-agent-sdk/src/cli-historical-transport.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/cli-historical-transport.ts b/packages/cloud-agent-sdk/src/cli-historical-transport.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/cli-historical-transport.ts rename to packages/cloud-agent-sdk/src/cli-historical-transport.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/cli-live-transport.test.ts b/packages/cloud-agent-sdk/src/cli-live-transport.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/cli-live-transport.test.ts rename to packages/cloud-agent-sdk/src/cli-live-transport.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/cli-live-transport.ts b/packages/cloud-agent-sdk/src/cli-live-transport.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/cli-live-transport.ts rename to packages/cloud-agent-sdk/src/cli-live-transport.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/cli-model.ts b/packages/cloud-agent-sdk/src/cli-model.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/cli-model.ts rename to packages/cloud-agent-sdk/src/cli-model.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/cloud-agent-connection.test.ts b/packages/cloud-agent-sdk/src/cloud-agent-connection.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/cloud-agent-connection.test.ts rename to packages/cloud-agent-sdk/src/cloud-agent-connection.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/cloud-agent-connection.ts b/packages/cloud-agent-sdk/src/cloud-agent-connection.ts similarity index 98% rename from apps/web/src/lib/cloud-agent-sdk/cloud-agent-connection.ts rename to packages/cloud-agent-sdk/src/cloud-agent-connection.ts index d9a16a99ec..6864d78e89 100644 --- a/apps/web/src/lib/cloud-agent-sdk/cloud-agent-connection.ts +++ b/packages/cloud-agent-sdk/src/cloud-agent-connection.ts @@ -3,7 +3,7 @@ import { isStreamError, type CloudAgentEvent, type StreamError, -} from '@/lib/cloud-agent-next/event-types'; +} from './event-types'; import { createBaseConnection, type Connection, diff --git a/apps/web/src/lib/cloud-agent-sdk/cloud-agent-transport.test.ts b/packages/cloud-agent-sdk/src/cloud-agent-transport.test.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/cloud-agent-transport.test.ts rename to packages/cloud-agent-sdk/src/cloud-agent-transport.test.ts index 85aa373e43..b0983f8f5f 100644 --- a/apps/web/src/lib/cloud-agent-sdk/cloud-agent-transport.test.ts +++ b/packages/cloud-agent-sdk/src/cloud-agent-transport.test.ts @@ -2,7 +2,7 @@ * Tests for CloudAgentTransport — verifies event normalization, routing to * chat/service sinks, and lifecycle generation tracking. */ -import type { CloudAgentEvent } from '@/lib/cloud-agent-next/event-types'; +import type { CloudAgentEvent } from './event-types'; import { createEventHelpers } from './__fixtures__/helpers'; import type { ChatEvent, ServiceEvent } from './normalizer'; import { createCloudAgentTransport } from './cloud-agent-transport'; diff --git a/apps/web/src/lib/cloud-agent-sdk/cloud-agent-transport.ts b/packages/cloud-agent-sdk/src/cloud-agent-transport.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/cloud-agent-transport.ts rename to packages/cloud-agent-sdk/src/cloud-agent-transport.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/context-usage.test.ts b/packages/cloud-agent-sdk/src/context-usage.test.ts similarity index 98% rename from apps/web/src/lib/cloud-agent-sdk/context-usage.test.ts rename to packages/cloud-agent-sdk/src/context-usage.test.ts index 61c0e14364..e806f255bd 100644 --- a/apps/web/src/lib/cloud-agent-sdk/context-usage.test.ts +++ b/packages/cloud-agent-sdk/src/context-usage.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage, UserMessage } from '@/types/opencode.gen'; +import type { AssistantMessage, UserMessage } from '@kilocode/app-shared/opencode'; import { calculateContextUsagePercentage, findLatestContextUsage } from './context-usage'; function assistantMessage(overrides: Partial = {}): AssistantMessage { diff --git a/apps/web/src/lib/cloud-agent-sdk/context-usage.ts b/packages/cloud-agent-sdk/src/context-usage.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/context-usage.ts rename to packages/cloud-agent-sdk/src/context-usage.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/create-session.test.ts b/packages/cloud-agent-sdk/src/create-session.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/create-session.test.ts rename to packages/cloud-agent-sdk/src/create-session.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/create-session.ts b/packages/cloud-agent-sdk/src/create-session.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/create-session.ts rename to packages/cloud-agent-sdk/src/create-session.ts diff --git a/packages/cloud-agent-sdk/src/event-types.ts b/packages/cloud-agent-sdk/src/event-types.ts new file mode 100644 index 0000000000..eaf864738a --- /dev/null +++ b/packages/cloud-agent-sdk/src/event-types.ts @@ -0,0 +1,92 @@ +/** + * Cloud Agent Event Types + * + * Type definitions and validation for WebSocket stream events. + * Events follow the OpenNext message format. + */ + +/** + * Event received from the cloud agent WebSocket stream. + */ +export type CloudAgentEvent = { + eventId: number; + executionId?: string | null; + sessionId: string; + streamEventType: string; + timestamp: string; + data: unknown; +}; + +/** + * Error codes for WebSocket protocol errors. + * These match the WS_* codes from cloud-agent-next/src/websocket/types.ts. + */ +export type StreamErrorCode = + | 'WS_PROTOCOL_ERROR' + | 'WS_AUTH_ERROR' + | 'WS_SESSION_NOT_FOUND' + | 'WS_EXECUTION_NOT_FOUND' + | 'WS_DUPLICATE_CONNECTION' + | 'WS_INTERNAL_ERROR'; + +const STREAM_ERROR_CODES: ReadonlySet = new Set([ + 'WS_PROTOCOL_ERROR', + 'WS_AUTH_ERROR', + 'WS_SESSION_NOT_FOUND', + 'WS_EXECUTION_NOT_FOUND', + 'WS_DUPLICATE_CONNECTION', + 'WS_INTERNAL_ERROR', +]); + +/** + * Error sent over the WebSocket stream. + */ +export type StreamError = { + type: 'error'; + code: StreamErrorCode; + message: string; +}; + +/** + * Type guard to validate a CloudAgentEvent from unknown data. + */ +export function isValidCloudAgentEvent(event: unknown): event is CloudAgentEvent { + if (typeof event !== 'object' || event === null) { + return false; + } + const hasValidExecutionId = + !('executionId' in event) || + event.executionId === undefined || + event.executionId === null || + typeof event.executionId === 'string'; + return ( + 'eventId' in event && + typeof event.eventId === 'number' && + hasValidExecutionId && + 'sessionId' in event && + typeof event.sessionId === 'string' && + 'streamEventType' in event && + typeof event.streamEventType === 'string' && + 'timestamp' in event && + typeof event.timestamp === 'string' && + 'data' in event + ); +} + +/** + * Type guard to validate a StreamError from unknown data. + */ +export function isStreamError(data: unknown): data is StreamError { + if (typeof data !== 'object' || data === null) { + return false; + } + return ( + 'type' in data && + data.type === 'error' && + 'code' in data && + typeof data.code === 'string' && + STREAM_ERROR_CODES.has(data.code) && + 'message' in data && + typeof data.message === 'string' + ); +} diff --git a/apps/web/src/lib/cloud-agent-sdk/exit-session.test.ts b/packages/cloud-agent-sdk/src/exit-session.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/exit-session.test.ts rename to packages/cloud-agent-sdk/src/exit-session.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/exit-session.ts b/packages/cloud-agent-sdk/src/exit-session.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/exit-session.ts rename to packages/cloud-agent-sdk/src/exit-session.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/fixture-runner.test.ts b/packages/cloud-agent-sdk/src/fixture-runner.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/fixture-runner.test.ts rename to packages/cloud-agent-sdk/src/fixture-runner.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/index.ts b/packages/cloud-agent-sdk/src/index.ts similarity index 97% rename from apps/web/src/lib/cloud-agent-sdk/index.ts rename to packages/cloud-agent-sdk/src/index.ts index 257e8f82a0..8b80113a4c 100644 --- a/apps/web/src/lib/cloud-agent-sdk/index.ts +++ b/packages/cloud-agent-sdk/src/index.ts @@ -1,3 +1,6 @@ +export { isStreamError, isValidCloudAgentEvent } from './event-types'; +export type { CloudAgentEvent, StreamError, StreamErrorCode } from './event-types'; + export { formatError as formatSessionError } from './session-manager'; export { createSessionManager } from './session-manager'; export { CLI_MODEL_ID, cliModelLabel } from './cli-model'; diff --git a/apps/web/src/lib/cloud-agent-sdk/message-id.ts b/packages/cloud-agent-sdk/src/message-id.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/message-id.ts rename to packages/cloud-agent-sdk/src/message-id.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/normalizer.test.ts b/packages/cloud-agent-sdk/src/normalizer.test.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/normalizer.test.ts rename to packages/cloud-agent-sdk/src/normalizer.test.ts index 71e22d2f2f..58aba912d7 100644 --- a/apps/web/src/lib/cloud-agent-sdk/normalizer.test.ts +++ b/packages/cloud-agent-sdk/src/normalizer.test.ts @@ -1,5 +1,5 @@ import { normalize, normalizeCliEvent, isChatEvent } from './normalizer'; -import type { CloudAgentEvent } from '@/lib/cloud-agent-next/event-types'; +import type { CloudAgentEvent } from './event-types'; function createRaw(streamEventType: string, data: unknown, sessionId = 'ses-1'): CloudAgentEvent { return { diff --git a/apps/web/src/lib/cloud-agent-sdk/normalizer.ts b/packages/cloud-agent-sdk/src/normalizer.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/normalizer.ts rename to packages/cloud-agent-sdk/src/normalizer.ts index 9e26c149dd..71accab233 100644 --- a/apps/web/src/lib/cloud-agent-sdk/normalizer.ts +++ b/packages/cloud-agent-sdk/src/normalizer.ts @@ -4,7 +4,7 @@ * boundary `as` casts so downstream code receives properly typed NormalizedEvents. */ import { z } from 'zod'; -import type { Part, SessionStatus, QuestionInfo, Message } from '@/types/opencode.gen'; +import type { Part, SessionStatus, QuestionInfo, Message } from '@kilocode/app-shared/opencode'; import type { SessionInfo, CloudStatus, diff --git a/apps/web/src/lib/cloud-agent-sdk/part-utils.ts b/packages/cloud-agent-sdk/src/part-utils.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/part-utils.ts rename to packages/cloud-agent-sdk/src/part-utils.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/preparation-attempts.test.ts b/packages/cloud-agent-sdk/src/preparation-attempts.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/preparation-attempts.test.ts rename to packages/cloud-agent-sdk/src/preparation-attempts.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/preparation-attempts.ts b/packages/cloud-agent-sdk/src/preparation-attempts.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/preparation-attempts.ts rename to packages/cloud-agent-sdk/src/preparation-attempts.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/reducer.test.ts b/packages/cloud-agent-sdk/src/reducer.test.ts similarity index 96% rename from apps/web/src/lib/cloud-agent-sdk/reducer.test.ts rename to packages/cloud-agent-sdk/src/reducer.test.ts index 125af236f9..84f09495a7 100644 --- a/apps/web/src/lib/cloud-agent-sdk/reducer.test.ts +++ b/packages/cloud-agent-sdk/src/reducer.test.ts @@ -1,7 +1,7 @@ import { reduce } from './reducer'; import type { ChatEvent } from './normalizer'; import type { StorageMutation } from './storage/types'; -import type { Message, Part } from '@/types/opencode.gen'; +import type { Message, Part } from '@kilocode/app-shared/opencode'; describe('reduce', () => { describe('message.updated', () => { diff --git a/apps/web/src/lib/cloud-agent-sdk/reducer.ts b/packages/cloud-agent-sdk/src/reducer.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/reducer.ts rename to packages/cloud-agent-sdk/src/reducer.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/remote-command-catalog.test.ts b/packages/cloud-agent-sdk/src/remote-command-catalog.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/remote-command-catalog.test.ts rename to packages/cloud-agent-sdk/src/remote-command-catalog.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/remote-command-catalog.ts b/packages/cloud-agent-sdk/src/remote-command-catalog.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/remote-command-catalog.ts rename to packages/cloud-agent-sdk/src/remote-command-catalog.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/remote-model-catalog.test.ts b/packages/cloud-agent-sdk/src/remote-model-catalog.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/remote-model-catalog.test.ts rename to packages/cloud-agent-sdk/src/remote-model-catalog.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/remote-model-catalog.ts b/packages/cloud-agent-sdk/src/remote-model-catalog.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/remote-model-catalog.ts rename to packages/cloud-agent-sdk/src/remote-model-catalog.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/remote-model-order.ts b/packages/cloud-agent-sdk/src/remote-model-order.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/remote-model-order.ts rename to packages/cloud-agent-sdk/src/remote-model-order.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/runtime.test.ts b/packages/cloud-agent-sdk/src/runtime.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/runtime.test.ts rename to packages/cloud-agent-sdk/src/runtime.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/runtime.ts b/packages/cloud-agent-sdk/src/runtime.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/runtime.ts rename to packages/cloud-agent-sdk/src/runtime.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/schemas.test.ts b/packages/cloud-agent-sdk/src/schemas.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/schemas.test.ts rename to packages/cloud-agent-sdk/src/schemas.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/schemas.ts b/packages/cloud-agent-sdk/src/schemas.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/schemas.ts rename to packages/cloud-agent-sdk/src/schemas.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/service-state.test.ts b/packages/cloud-agent-sdk/src/service-state.test.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/service-state.test.ts rename to packages/cloud-agent-sdk/src/service-state.test.ts index 082be743a8..22ce9662aa 100644 --- a/apps/web/src/lib/cloud-agent-sdk/service-state.test.ts +++ b/packages/cloud-agent-sdk/src/service-state.test.ts @@ -1,6 +1,6 @@ import { createServiceState } from './service-state'; import type { ServiceStateConfig } from './service-state'; -import type { Session, QuestionInfo } from '@/types/opencode.gen'; +import type { Session, QuestionInfo } from '@kilocode/app-shared/opencode'; function makeConfig(overrides?: Partial): ServiceStateConfig { return { rootSessionId: 'root-1', ...overrides }; diff --git a/apps/web/src/lib/cloud-agent-sdk/service-state.ts b/packages/cloud-agent-sdk/src/service-state.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/service-state.ts rename to packages/cloud-agent-sdk/src/service-state.ts index f68c203b71..fc587e8575 100644 --- a/apps/web/src/lib/cloud-agent-sdk/service-state.ts +++ b/packages/cloud-agent-sdk/src/service-state.ts @@ -5,7 +5,7 @@ * Processes ServiceEvents from the normalizer and provides a reactive snapshot * of the current service state via subscribe(). */ -import type { QuestionInfo } from '@/types/opencode.gen'; +import type { QuestionInfo } from '@kilocode/app-shared/opencode'; import type { ServiceEvent } from './normalizer'; import type { SessionInfo, diff --git a/apps/web/src/lib/cloud-agent-sdk/session-manager.test.ts b/packages/cloud-agent-sdk/src/session-manager.test.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/session-manager.test.ts rename to packages/cloud-agent-sdk/src/session-manager.test.ts index d8fdcd52c7..92dd16bfc3 100644 --- a/apps/web/src/lib/cloud-agent-sdk/session-manager.test.ts +++ b/packages/cloud-agent-sdk/src/session-manager.test.ts @@ -22,7 +22,7 @@ import type { CloudAgentSessionDismissSuggestionInput, } from './session'; import type { JotaiSessionStorage } from './storage/jotai'; -import type { AssistantMessage, UserMessage } from '@/types/opencode.gen'; +import type { AssistantMessage, UserMessage } from '@kilocode/app-shared/opencode'; import { kiloId, cloudAgentId, stubUserMessage, stubTextPart, makeSnapshot } from './test-helpers'; import type { CloudStatus, diff --git a/apps/web/src/lib/cloud-agent-sdk/session-manager.ts b/packages/cloud-agent-sdk/src/session-manager.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/session-manager.ts rename to packages/cloud-agent-sdk/src/session-manager.ts index 6243164675..1fc8ba9427 100644 --- a/apps/web/src/lib/cloud-agent-sdk/session-manager.ts +++ b/packages/cloud-agent-sdk/src/session-manager.ts @@ -1,5 +1,5 @@ -import type { CloudAgentAttachments } from '@/lib/cloud-agent/constants'; -import type { Images } from '@/lib/images-schema'; +import type { CloudAgentAttachments } from '@kilocode/app-shared/cloud-agent'; +import type { Images } from '@kilocode/app-shared/images-schema'; import { errorShapeSchema } from './schemas'; import type { RemoteAttachmentPart, @@ -50,7 +50,7 @@ import type { OlderMessagesError, PreparationAttempt, } from './types'; -import type { QuestionInfo } from '@/types/opencode.gen'; +import type { QuestionInfo } from '@kilocode/app-shared/opencode'; import { splitByContiguousPrefix } from './array-utils'; import type { UserWebConnection } from './user-web-connection'; import { generateMessageId } from './message-id'; diff --git a/apps/web/src/lib/cloud-agent-sdk/session-phase.test.ts b/packages/cloud-agent-sdk/src/session-phase.test.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/session-phase.test.ts rename to packages/cloud-agent-sdk/src/session-phase.test.ts index 3bb41d5dd0..7bf23154ec 100644 --- a/apps/web/src/lib/cloud-agent-sdk/session-phase.test.ts +++ b/packages/cloud-agent-sdk/src/session-phase.test.ts @@ -8,7 +8,7 @@ import { createCloudAgentSession } from './session'; import { createEventHelpers, sessionInfo } from './__fixtures__/helpers'; import { kiloId, cloudAgentId, makeSnapshot } from './test-helpers'; import type { SessionActivity, AgentStatus, SessionInfo } from './types'; -import type { CloudAgentEvent } from '@/lib/cloud-agent-next/event-types'; +import type { CloudAgentEvent } from './event-types'; // --------------------------------------------------------------------------- // WebSocket mock diff --git a/apps/web/src/lib/cloud-agent-sdk/session-routing.test.ts b/packages/cloud-agent-sdk/src/session-routing.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/session-routing.test.ts rename to packages/cloud-agent-sdk/src/session-routing.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/session-transport.test.ts b/packages/cloud-agent-sdk/src/session-transport.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/session-transport.test.ts rename to packages/cloud-agent-sdk/src/session-transport.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/session.test.ts b/packages/cloud-agent-sdk/src/session.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/session.test.ts rename to packages/cloud-agent-sdk/src/session.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/session.ts b/packages/cloud-agent-sdk/src/session.ts similarity index 98% rename from apps/web/src/lib/cloud-agent-sdk/session.ts rename to packages/cloud-agent-sdk/src/session.ts index bc8e7412f1..03b8a599b2 100644 --- a/apps/web/src/lib/cloud-agent-sdk/session.ts +++ b/packages/cloud-agent-sdk/src/session.ts @@ -5,9 +5,9 @@ * `resolveSession` determines the session type and routes to Cloud Agent, * CLI live, or CLI historical transport. */ -import type { QuestionInfo } from '@/types/opencode.gen'; -import type { CloudAgentAttachments } from '@/lib/cloud-agent/constants'; -import type { Images } from '@/lib/images-schema'; +import type { QuestionInfo } from '@kilocode/app-shared/opencode'; +import type { CloudAgentAttachments } from '@kilocode/app-shared/cloud-agent'; +import type { Images } from '@kilocode/app-shared/images-schema'; import type { NormalizedEvent } from './normalizer'; import type { SuggestionAction } from './types'; import type { RemoteModelOverride, RemoteModelState } from './remote-model-catalog'; diff --git a/apps/web/src/lib/cloud-agent-sdk/storage/helpers.test.ts b/packages/cloud-agent-sdk/src/storage/helpers.test.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/storage/helpers.test.ts rename to packages/cloud-agent-sdk/src/storage/helpers.test.ts index 6e0df95a34..2bba0cdfb8 100644 --- a/apps/web/src/lib/cloud-agent-sdk/storage/helpers.test.ts +++ b/packages/cloud-agent-sdk/src/storage/helpers.test.ts @@ -1,4 +1,4 @@ -import type { Part } from '@/types/opencode.gen'; +import type { Part } from '@kilocode/app-shared/opencode'; import { EMPTY_PARTS, applyTextDelta, diff --git a/apps/web/src/lib/cloud-agent-sdk/storage/helpers.ts b/packages/cloud-agent-sdk/src/storage/helpers.ts similarity index 97% rename from apps/web/src/lib/cloud-agent-sdk/storage/helpers.ts rename to packages/cloud-agent-sdk/src/storage/helpers.ts index 8a618aba41..dbaeee24a9 100644 --- a/apps/web/src/lib/cloud-agent-sdk/storage/helpers.ts +++ b/packages/cloud-agent-sdk/src/storage/helpers.ts @@ -1,4 +1,4 @@ -import type { Part, TextPart } from '@/types/opencode.gen'; +import type { Part, TextPart } from '@kilocode/app-shared/opencode'; function insertSorted(arr: string[], id: string): string[] { const result = [...arr]; diff --git a/apps/web/src/lib/cloud-agent-sdk/storage/jotai.test.ts b/packages/cloud-agent-sdk/src/storage/jotai.test.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/storage/jotai.test.ts rename to packages/cloud-agent-sdk/src/storage/jotai.test.ts index e459911f41..c6e288b2e3 100644 --- a/apps/web/src/lib/cloud-agent-sdk/storage/jotai.test.ts +++ b/packages/cloud-agent-sdk/src/storage/jotai.test.ts @@ -1,6 +1,6 @@ import { createStore } from 'jotai'; import { createJotaiStorage } from './jotai'; -import type { Part } from '@/types/opencode.gen'; +import type { Part } from '@kilocode/app-shared/opencode'; import type { MessageInfo } from '../types'; function makeMsg(id: string, role: 'user' | 'assistant' = 'user'): MessageInfo { diff --git a/apps/web/src/lib/cloud-agent-sdk/storage/jotai.ts b/packages/cloud-agent-sdk/src/storage/jotai.ts similarity index 98% rename from apps/web/src/lib/cloud-agent-sdk/storage/jotai.ts rename to packages/cloud-agent-sdk/src/storage/jotai.ts index ea1e8fac4d..e58ff38998 100644 --- a/apps/web/src/lib/cloud-agent-sdk/storage/jotai.ts +++ b/packages/cloud-agent-sdk/src/storage/jotai.ts @@ -1,7 +1,7 @@ import { atom } from 'jotai'; import type { Atom } from 'jotai'; import type { createStore } from 'jotai'; -import type { Part } from '@/types/opencode.gen'; +import type { Part } from '@kilocode/app-shared/opencode'; import type { MessageInfo } from '../types'; import type { SessionStorage } from './types'; import { diff --git a/apps/web/src/lib/cloud-agent-sdk/storage/memory.test.ts b/packages/cloud-agent-sdk/src/storage/memory.test.ts similarity index 99% rename from apps/web/src/lib/cloud-agent-sdk/storage/memory.test.ts rename to packages/cloud-agent-sdk/src/storage/memory.test.ts index 2cf309b943..5ec38cac4f 100644 --- a/apps/web/src/lib/cloud-agent-sdk/storage/memory.test.ts +++ b/packages/cloud-agent-sdk/src/storage/memory.test.ts @@ -1,6 +1,6 @@ import { createMemoryStorage } from './memory'; import type { SessionStorage } from './types'; -import type { Part } from '@/types/opencode.gen'; +import type { Part } from '@kilocode/app-shared/opencode'; import type { MessageInfo } from '../types'; function makeMsg(id: string, role: 'user' | 'assistant' = 'user'): MessageInfo { diff --git a/apps/web/src/lib/cloud-agent-sdk/storage/memory.ts b/packages/cloud-agent-sdk/src/storage/memory.ts similarity index 98% rename from apps/web/src/lib/cloud-agent-sdk/storage/memory.ts rename to packages/cloud-agent-sdk/src/storage/memory.ts index acadcbb015..4f9b315796 100644 --- a/apps/web/src/lib/cloud-agent-sdk/storage/memory.ts +++ b/packages/cloud-agent-sdk/src/storage/memory.ts @@ -1,4 +1,4 @@ -import type { Part } from '@/types/opencode.gen'; +import type { Part } from '@kilocode/app-shared/opencode'; import type { MessageInfo } from '../types'; import type { SessionStorage } from './types'; import { diff --git a/apps/web/src/lib/cloud-agent-sdk/storage/types.ts b/packages/cloud-agent-sdk/src/storage/types.ts similarity index 96% rename from apps/web/src/lib/cloud-agent-sdk/storage/types.ts rename to packages/cloud-agent-sdk/src/storage/types.ts index 2f200c2a8f..067b5efa08 100644 --- a/apps/web/src/lib/cloud-agent-sdk/storage/types.ts +++ b/packages/cloud-agent-sdk/src/storage/types.ts @@ -1,4 +1,4 @@ -import type { Part } from '@/types/opencode.gen'; +import type { Part } from '@kilocode/app-shared/opencode'; import type { MessageInfo } from '../types'; /** diff --git a/apps/web/src/lib/cloud-agent-sdk/test-helpers.ts b/packages/cloud-agent-sdk/src/test-helpers.ts similarity index 94% rename from apps/web/src/lib/cloud-agent-sdk/test-helpers.ts rename to packages/cloud-agent-sdk/src/test-helpers.ts index 89cfec50b8..1e2ee834d0 100644 --- a/apps/web/src/lib/cloud-agent-sdk/test-helpers.ts +++ b/packages/cloud-agent-sdk/src/test-helpers.ts @@ -2,8 +2,8 @@ import { normalize, isChatEvent } from './normalizer'; import { createMemoryStorage } from './storage/memory'; import { createChatProcessor } from './chat-processor'; import { createServiceState } from './service-state'; -import type { CloudAgentEvent } from '@/lib/cloud-agent-next/event-types'; -import type { UserMessage, TextPart } from '@/types/opencode.gen'; +import type { CloudAgentEvent } from './event-types'; +import type { UserMessage, TextPart } from '@kilocode/app-shared/opencode'; import type { KiloSessionId, CloudAgentSessionId, diff --git a/apps/web/src/lib/cloud-agent-sdk/transport.ts b/packages/cloud-agent-sdk/src/transport.ts similarity index 97% rename from apps/web/src/lib/cloud-agent-sdk/transport.ts rename to packages/cloud-agent-sdk/src/transport.ts index 032b0edd7f..b28b9d6a0f 100644 --- a/apps/web/src/lib/cloud-agent-sdk/transport.ts +++ b/packages/cloud-agent-sdk/src/transport.ts @@ -5,8 +5,8 @@ * Command methods are optional — present only on interactive transports. */ import type { ChatEvent, ServiceEvent } from './normalizer'; -import type { CloudAgentAttachments } from '@/lib/cloud-agent/constants'; -import type { Images } from '@/lib/images-schema'; +import type { CloudAgentAttachments } from '@kilocode/app-shared/cloud-agent'; +import type { Images } from '@kilocode/app-shared/images-schema'; import type { CloudAgentSessionId, KiloSessionId } from './types'; import type { ModelRef, RemoteModelOverride } from './remote-model-catalog'; diff --git a/apps/web/src/lib/cloud-agent-sdk/types.ts b/packages/cloud-agent-sdk/src/types.ts similarity index 97% rename from apps/web/src/lib/cloud-agent-sdk/types.ts rename to packages/cloud-agent-sdk/src/types.ts index 27659f9244..dd3e1cf630 100644 --- a/apps/web/src/lib/cloud-agent-sdk/types.ts +++ b/packages/cloud-agent-sdk/src/types.ts @@ -20,9 +20,9 @@ export type { EventSessionStatus, EventSessionCreated, EventSessionUpdated, -} from '@/types/opencode.gen'; +} from '@kilocode/app-shared/opencode'; -import type { UserMessage, AssistantMessage, Part } from '@/types/opencode.gen'; +import type { UserMessage, AssistantMessage, Part } from '@kilocode/app-shared/opencode'; export type { KiloSdkMessageHistory, @@ -68,7 +68,7 @@ export type SessionPhase = // Service state types — separated from chat data // --------------------------------------------------------------------------- -import type { QuestionInfo } from '@/types/opencode.gen'; +import type { QuestionInfo } from '@kilocode/app-shared/opencode'; /** Real-time activity indicator — renders as a separate spinner/indicator. */ export type SessionActivity = diff --git a/apps/web/src/lib/cloud-agent-sdk/user-web-connection.test.ts b/packages/cloud-agent-sdk/src/user-web-connection.test.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/user-web-connection.test.ts rename to packages/cloud-agent-sdk/src/user-web-connection.test.ts diff --git a/apps/web/src/lib/cloud-agent-sdk/user-web-connection.ts b/packages/cloud-agent-sdk/src/user-web-connection.ts similarity index 100% rename from apps/web/src/lib/cloud-agent-sdk/user-web-connection.ts rename to packages/cloud-agent-sdk/src/user-web-connection.ts diff --git a/packages/cloud-agent-sdk/tsconfig.json b/packages/cloud-agent-sdk/tsconfig.json new file mode 100644 index 0000000000..dc7f74e23a --- /dev/null +++ b/packages/cloud-agent-sdk/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "lib": ["ES2022", "DOM"], + "moduleResolution": "bundler", + "types": ["jest", "node"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "noEmit": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94de1a80ce..68fb9ea6d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -664,6 +664,9 @@ importers: '@kilocode/cloud-agent-profile': specifier: workspace:* version: link:../../packages/cloud-agent-profile + '@kilocode/cloud-agent-sdk': + specifier: workspace:* + version: link:../../packages/cloud-agent-sdk '@kilocode/db': specifier: workspace:* version: link:../../packages/db @@ -1120,6 +1123,10 @@ importers: version: 5.9.3 packages/app-shared: + dependencies: + zod: + specifier: 'catalog:' + version: 4.4.3 devDependencies: '@typescript/native-preview': specifier: 'catalog:' @@ -1184,6 +1191,40 @@ importers: specifier: 'catalog:' version: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@24.12.4)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + packages/cloud-agent-sdk: + dependencies: + '@kilocode/app-shared': + specifier: workspace:* + version: link:../app-shared + jotai: + specifier: 2.18.1 + version: 2.18.1(@babel/core@7.29.0)(@babel/template@7.29.7)(@types/react@19.2.14)(react@19.2.6) + zod: + specifier: 'catalog:' + version: 4.4.3 + devDependencies: + '@jest/globals': + specifier: 30.3.0 + version: 30.3.0 + '@kilocode/session-ingest-contracts': + specifier: workspace:* + version: link:../session-ingest-contracts + '@swc/jest': + specifier: 0.2.39 + version: 0.2.39(@swc/core@1.15.18) + '@types/jest': + specifier: 30.0.0 + version: 30.0.0 + '@typescript/native-preview': + specifier: 'catalog:' + version: 7.0.0-dev.20260514.1 + jest: + specifier: 30.3.0 + version: 30.3.0(@types/node@24.12.4)(node-notifier@10.0.1) + typescript: + specifier: 'catalog:' + version: 5.9.3 + packages/container-usage: dependencies: zod: @@ -4477,11 +4518,11 @@ packages: '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} - deprecated: 'Merged into tsx: https://tsx.hirok.io' + deprecated: 'Merged into tsx: https://tsx.is' '@esbuild-kit/esm-loader@2.6.5': resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} - deprecated: 'Merged into tsx: https://tsx.hirok.io' + deprecated: 'Merged into tsx: https://tsx.is' '@esbuild/aix-ppc64@0.27.4': resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} From 1c6fdbd3818fda1684d46de887037f60d40a44c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 23 Jul 2026 06:17:13 +0200 Subject: [PATCH 03/14] refactor(cloud-agent-sdk): own SendMessagePayload in the package and re-export from web --- apps/web/src/lib/cloud-agent-next/types.ts | 14 +------------- packages/cloud-agent-sdk/src/index.ts | 1 + packages/cloud-agent-sdk/src/types.ts | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/apps/web/src/lib/cloud-agent-next/types.ts b/apps/web/src/lib/cloud-agent-next/types.ts index f6f862e904..0a95e0031d 100644 --- a/apps/web/src/lib/cloud-agent-next/types.ts +++ b/apps/web/src/lib/cloud-agent-next/types.ts @@ -1,13 +1 @@ -export type SendMessagePayload = - | { - type: 'prompt'; - prompt: string; - mode: string; - model: string; - variant?: string; - } - | { - type: 'command'; - command: string; - arguments: string; - }; +export type { SendMessagePayload } from '@kilocode/cloud-agent-sdk'; diff --git a/packages/cloud-agent-sdk/src/index.ts b/packages/cloud-agent-sdk/src/index.ts index 8b80113a4c..9d1aedc34c 100644 --- a/packages/cloud-agent-sdk/src/index.ts +++ b/packages/cloud-agent-sdk/src/index.ts @@ -184,4 +184,5 @@ export type { Session, SessionStatus, QuestionInfo, + SendMessagePayload, } from './types'; diff --git a/packages/cloud-agent-sdk/src/types.ts b/packages/cloud-agent-sdk/src/types.ts index dd3e1cf630..66073b2f5c 100644 --- a/packages/cloud-agent-sdk/src/types.ts +++ b/packages/cloud-agent-sdk/src/types.ts @@ -30,6 +30,24 @@ export type { KiloSdkStoredMessage, } from '@kilocode/session-ingest-contracts'; +/** + * Payload sent to a cloud agent session transport — either a chat prompt or a + * slash command. Pairs with the SDK transport send types. + */ +export type SendMessagePayload = + | { + type: 'prompt'; + prompt: string; + mode: string; + model: string; + variant?: string; + } + | { + type: 'command'; + command: string; + arguments: string; + }; + // --------------------------------------------------------------------------- // Branded session ID types — prevent accidental mixing of kilo vs cloud agent IDs // --------------------------------------------------------------------------- From 303755560f080fe1fd578733e026abde0efac97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 23 Jul 2026 06:23:29 +0200 Subject: [PATCH 04/14] refactor(mobile): resolve cloud-agent-sdk from the package and drop the divergent resolvers --- apps/mobile/metro.config.js | 22 +---------- apps/mobile/package.json | 1 + .../src/app/(app)/agent-chat/[session-id].tsx | 2 +- .../chat-composer-exit-slash-command.test.ts | 4 +- ...ser-slash-commands-new-reservation.test.ts | 2 +- .../chat-composer-slash-commands.test.ts | 4 +- .../agents/chat-composer-slash-commands.ts | 4 +- .../src/components/agents/chat-composer.tsx | 4 +- .../agents/child-session-card-state.test.ts | 7 +++- .../agents/child-session-card-state.ts | 7 +++- .../agents/child-session-section.tsx | 7 +++- .../agents/child-session-sheet-state.ts | 2 +- .../components/agents/child-session-sheet.tsx | 2 +- .../src/components/agents/compute-status.ts | 2 +- .../create-and-navigate-agent-session.test.ts | 2 +- .../create-and-navigate-agent-session.ts | 2 +- ...reate-remote-session-with-feedback.test.ts | 2 +- .../create-remote-session-with-feedback.ts | 2 +- .../components/agents/file-part-renderer.tsx | 2 +- .../components/agents/message-bubble.test.ts | 2 +- .../src/components/agents/message-bubble.tsx | 2 +- .../agents/mobile-session-manager-helpers.ts | 2 +- .../agents/mobile-session-manager.ts | 2 +- .../mobile-session-page-adapter.test.ts | 2 +- .../agents/mobile-session-page-adapter.ts | 2 +- .../mobile-session-transport-payload.ts | 3 +- .../components/agents/part-renderer.test.ts | 2 +- .../src/components/agents/part-renderer.tsx | 2 +- .../src/components/agents/part-types.test.ts | 2 +- .../src/components/agents/part-types.ts | 2 +- .../components/agents/preparation-group.tsx | 2 +- .../agents/session-detail-content.tsx | 2 +- .../agents/session-detail-routes.test.ts | 2 +- .../session-detail-send-attachment.test.ts | 2 +- .../agents/session-detail-send-attachment.ts | 2 +- .../agents/session-message-list-state.test.ts | 2 +- .../agents/session-message-list-state.ts | 2 +- .../agents/session-message-list.tsx | 2 +- ...ion-pagination-header-render-model.test.ts | 2 +- .../components/agents/session-provider.tsx | 2 +- .../agents/session-status-indicator.tsx | 2 +- .../components/agents/session-transcript.ts | 4 +- .../agents/slash-command-suggestions.tsx | 2 +- .../components/agents/suggest-tool-card.tsx | 2 +- .../src/components/agents/suggestion-card.tsx | 2 +- .../agents/tool-cards/bash-tool-card.tsx | 2 +- .../agents/tool-cards/edit-tool-card.tsx | 2 +- .../agents/tool-cards/generic-tool-card.tsx | 2 +- .../agents/tool-cards/glob-tool-card.tsx | 2 +- .../agents/tool-cards/grep-tool-card.tsx | 2 +- .../agents/tool-cards/list-tool-card.tsx | 2 +- .../agents/tool-cards/read-tool-card.tsx | 2 +- .../agents/tool-cards/task-tool-card.tsx | 2 +- .../agents/tool-cards/todo-tool-card.tsx | 2 +- .../tool-cards/web-search-tool-card.tsx | 2 +- .../agents/tool-cards/write-tool-card.tsx | 2 +- .../components/agents/tool-part-renderer.tsx | 2 +- .../src/components/agents/use-message-copy.ts | 2 +- .../agents/use-new-session-creator.ts | 2 +- .../agents/use-session-config-sync.ts | 2 +- .../agents/use-session-detail-rename.ts | 2 +- .../agents/user-web-connection-provider.tsx | 4 +- .../components/agents/working-indicator.tsx | 2 +- .../active-sessions-live-sync.test-helpers.ts | 2 +- .../src/lib/active-sessions-live-sync.ts | 2 +- apps/mobile/src/lib/active-sessions-live.ts | 2 +- apps/mobile/src/lib/cloud-agent-runtime.ts | 2 +- .../hooks/remote-instance-spawn-classifier.ts | 9 +++-- .../hooks/use-remote-instance-spawn.test.ts | 7 +++- .../lib/hooks/use-remote-instance-spawn.ts | 2 +- .../lib/hooks/use-session-model-options.ts | 4 +- .../src/lib/remote-submit-outcome.test.ts | 2 +- apps/mobile/src/lib/remote-submit-outcome.ts | 2 +- .../src/lib/session-context-info.test.ts | 2 +- apps/mobile/src/lib/session-context-info.ts | 5 ++- .../src/lib/use-session-model-options.test.ts | 2 +- .../src/lib/user-web-connection-lifecycle.ts | 2 +- apps/mobile/tsconfig.json | 8 +--- apps/mobile/vitest.config.ts | 39 ------------------- pnpm-lock.yaml | 3 ++ 80 files changed, 115 insertions(+), 153 deletions(-) diff --git a/apps/mobile/metro.config.js b/apps/mobile/metro.config.js index 741cfdd799..8d359e73c8 100644 --- a/apps/mobile/metro.config.js +++ b/apps/mobile/metro.config.js @@ -4,8 +4,6 @@ const { getSentryExpoConfig } = require('@sentry/react-native/metro'); const { withNativewind } = require('nativewind/metro'); const monorepoRoot = path.resolve(__dirname, '../..'); -const webSrc = path.resolve(monorepoRoot, 'apps', 'web', 'src'); -const cloudAgentSdkPath = path.resolve(webSrc, 'lib', 'cloud-agent-sdk'); /** @type {import('expo/metro-config').MetroConfig} */ const config = getSentryExpoConfig(__dirname); @@ -13,30 +11,12 @@ const config = getSentryExpoConfig(__dirname); // Allow Metro to resolve workspace files and pnpm's real package paths config.watchFolders = [...new Set([...(config.watchFolders || []), monorepoRoot])]; -// Let SDK dependencies (jotai, zod, etc.) resolve from the monorepo root node_modules +// Let workspace package dependencies (jotai, zod, etc.) resolve from the monorepo root node_modules config.resolver.nodeModulesPaths = [ ...(config.resolver.nodeModulesPaths || []), path.resolve(monorepoRoot, 'node_modules'), ]; -// Allow kilo-app code to `import { ... } from 'cloud-agent-sdk'` -config.resolver.extraNodeModules = { - ...config.resolver.extraNodeModules, - 'cloud-agent-sdk': cloudAgentSdkPath, -}; - -// Remap `@/` imports to the web app's src/ when originating from cloud-agent-sdk -config.resolver.resolveRequest = (context, moduleName, platform) => { - if ( - moduleName.startsWith('@/') && - context.originModulePath.includes('src/lib/cloud-agent-sdk/') - ) { - const remapped = path.resolve(webSrc, moduleName.slice(2)); - return context.resolveRequest(context, remapped, platform); - } - return context.resolveRequest(context, moduleName, platform); -}; - module.exports = withNativewind(config, { inlineVariables: false, }); diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 9d533c25a6..78ba023261 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -24,6 +24,7 @@ "@expo-google-fonts/jetbrains-mono": "0.4.1", "@expo/react-native-action-sheet": "4.1.1", "@kilocode/app-shared": "workspace:*", + "@kilocode/cloud-agent-sdk": "workspace:*", "@kilocode/event-service": "workspace:*", "@kilocode/kilo-chat": "workspace:*", "@kilocode/kilo-chat-hooks": "workspace:*", diff --git a/apps/mobile/src/app/(app)/agent-chat/[session-id].tsx b/apps/mobile/src/app/(app)/agent-chat/[session-id].tsx index e2017c75cc..ea983ca0c2 100644 --- a/apps/mobile/src/app/(app)/agent-chat/[session-id].tsx +++ b/apps/mobile/src/app/(app)/agent-chat/[session-id].tsx @@ -1,4 +1,4 @@ -import { type KiloSessionId } from 'cloud-agent-sdk'; +import { type KiloSessionId } from '@kilocode/cloud-agent-sdk'; import { type Href, useLocalSearchParams, useRouter } from 'expo-router'; import { useQuery } from '@tanstack/react-query'; import { View } from 'react-native'; diff --git a/apps/mobile/src/components/agents/chat-composer-exit-slash-command.test.ts b/apps/mobile/src/components/agents/chat-composer-exit-slash-command.test.ts index 50e35fd34d..7b45b0b458 100644 --- a/apps/mobile/src/components/agents/chat-composer-exit-slash-command.test.ts +++ b/apps/mobile/src/components/agents/chat-composer-exit-slash-command.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; -import { type SlashCommandInfo } from 'cloud-agent-sdk'; -import { type RemoteCommandState } from 'cloud-agent-sdk/remote-command-catalog'; +import { type SlashCommandInfo } from '@kilocode/cloud-agent-sdk'; +import { type RemoteCommandState } from '@kilocode/cloud-agent-sdk/remote-command-catalog'; import { createMobileSlashCommandList, diff --git a/apps/mobile/src/components/agents/chat-composer-slash-commands-new-reservation.test.ts b/apps/mobile/src/components/agents/chat-composer-slash-commands-new-reservation.test.ts index 29757d3c3c..930707cffa 100644 --- a/apps/mobile/src/components/agents/chat-composer-slash-commands-new-reservation.test.ts +++ b/apps/mobile/src/components/agents/chat-composer-slash-commands-new-reservation.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { type SlashCommandInfo } from 'cloud-agent-sdk'; +import { type SlashCommandInfo } from '@kilocode/cloud-agent-sdk'; import { LOCAL_NEW_SLASH_COMMAND, diff --git a/apps/mobile/src/components/agents/chat-composer-slash-commands.test.ts b/apps/mobile/src/components/agents/chat-composer-slash-commands.test.ts index 4797898f1f..e07f8ec326 100644 --- a/apps/mobile/src/components/agents/chat-composer-slash-commands.test.ts +++ b/apps/mobile/src/components/agents/chat-composer-slash-commands.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; -import { type SlashCommandInfo } from 'cloud-agent-sdk'; -import { type RemoteCommandState } from 'cloud-agent-sdk/remote-command-catalog'; +import { type SlashCommandInfo } from '@kilocode/cloud-agent-sdk'; +import { type RemoteCommandState } from '@kilocode/cloud-agent-sdk/remote-command-catalog'; import { createMobileSlashCommandList, diff --git a/apps/mobile/src/components/agents/chat-composer-slash-commands.ts b/apps/mobile/src/components/agents/chat-composer-slash-commands.ts index cdbeec7819..bc6c8600bf 100644 --- a/apps/mobile/src/components/agents/chat-composer-slash-commands.ts +++ b/apps/mobile/src/components/agents/chat-composer-slash-commands.ts @@ -1,5 +1,5 @@ -import { type ActiveSessionType, type SlashCommandInfo } from 'cloud-agent-sdk'; -import { type RemoteCommandState } from 'cloud-agent-sdk/remote-command-catalog'; +import { type ActiveSessionType, type SlashCommandInfo } from '@kilocode/cloud-agent-sdk'; +import { type RemoteCommandState } from '@kilocode/cloud-agent-sdk/remote-command-catalog'; /** * Local reserved /new command — surfaced only for remote sessions, never diff --git a/apps/mobile/src/components/agents/chat-composer.tsx b/apps/mobile/src/components/agents/chat-composer.tsx index c8e0a0af90..1be830472b 100644 --- a/apps/mobile/src/components/agents/chat-composer.tsx +++ b/apps/mobile/src/components/agents/chat-composer.tsx @@ -5,8 +5,8 @@ */ import * as Haptics from 'expo-haptics'; import { useActionSheet } from '@expo/react-native-action-sheet'; -import { type SlashCommandInfo } from 'cloud-agent-sdk'; -import { type RemoteCommandState } from 'cloud-agent-sdk/remote-command-catalog'; +import { type SlashCommandInfo } from '@kilocode/cloud-agent-sdk'; +import { type RemoteCommandState } from '@kilocode/cloud-agent-sdk/remote-command-catalog'; import { useCallback, useMemo, useRef, useState } from 'react'; import { Keyboard, diff --git a/apps/mobile/src/components/agents/child-session-card-state.test.ts b/apps/mobile/src/components/agents/child-session-card-state.test.ts index 167858ef8c..50d00d241b 100644 --- a/apps/mobile/src/components/agents/child-session-card-state.test.ts +++ b/apps/mobile/src/components/agents/child-session-card-state.test.ts @@ -1,5 +1,10 @@ /* eslint-disable max-lines -- test file with many fixtures */ -import { type KiloSessionId, type Part, type StoredMessage, type ToolPart } from 'cloud-agent-sdk'; +import { + type KiloSessionId, + type Part, + type StoredMessage, + type ToolPart, +} from '@kilocode/cloud-agent-sdk'; import { describe, expect, it } from 'vitest'; import { diff --git a/apps/mobile/src/components/agents/child-session-card-state.ts b/apps/mobile/src/components/agents/child-session-card-state.ts index 1111fa6ed8..a197949969 100644 --- a/apps/mobile/src/components/agents/child-session-card-state.ts +++ b/apps/mobile/src/components/agents/child-session-card-state.ts @@ -1,4 +1,9 @@ -import { type KiloSessionId, type Part, type StoredMessage, type ToolPart } from 'cloud-agent-sdk'; +import { + type KiloSessionId, + type Part, + type StoredMessage, + type ToolPart, +} from '@kilocode/cloud-agent-sdk'; import { computeStatus } from './compute-status'; import { isToolPart } from './part-types'; diff --git a/apps/mobile/src/components/agents/child-session-section.tsx b/apps/mobile/src/components/agents/child-session-section.tsx index ed17028a8c..c7432bf1dc 100644 --- a/apps/mobile/src/components/agents/child-session-section.tsx +++ b/apps/mobile/src/components/agents/child-session-section.tsx @@ -2,7 +2,12 @@ import { type ReactNode } from 'react'; import { Pressable, View } from 'react-native'; import { Bot, ChevronRight, Loader2 } from 'lucide-react-native'; import Animated, { LinearTransition } from 'react-native-reanimated'; -import { type KiloSessionId, type Part, type StoredMessage, type ToolPart } from 'cloud-agent-sdk'; +import { + type KiloSessionId, + type Part, + type StoredMessage, + type ToolPart, +} from '@kilocode/cloud-agent-sdk'; import { SpinningIcon } from '@/components/ui/spinning-icon'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/child-session-sheet-state.ts b/apps/mobile/src/components/agents/child-session-sheet-state.ts index e59206697b..c1720ff6a5 100644 --- a/apps/mobile/src/components/agents/child-session-sheet-state.ts +++ b/apps/mobile/src/components/agents/child-session-sheet-state.ts @@ -1,4 +1,4 @@ -import { type ChildSessionHydrationState } from 'cloud-agent-sdk'; +import { type ChildSessionHydrationState } from '@kilocode/cloud-agent-sdk'; type ChildSessionSheetState = 'loading' | 'empty' | 'error' | 'content'; diff --git a/apps/mobile/src/components/agents/child-session-sheet.tsx b/apps/mobile/src/components/agents/child-session-sheet.tsx index 1fd9aea13a..40fcb0e047 100644 --- a/apps/mobile/src/components/agents/child-session-sheet.tsx +++ b/apps/mobile/src/components/agents/child-session-sheet.tsx @@ -1,6 +1,6 @@ import { type ReactNode } from 'react'; import { Modal, View } from 'react-native'; -import { type ChildSessionHydrationState, type StoredMessage } from 'cloud-agent-sdk'; +import { type ChildSessionHydrationState, type StoredMessage } from '@kilocode/cloud-agent-sdk'; import { EmptyState } from '@/components/empty-state'; import { QueryError } from '@/components/query-error'; diff --git a/apps/mobile/src/components/agents/compute-status.ts b/apps/mobile/src/components/agents/compute-status.ts index 23d51d83b6..d3152fea9b 100644 --- a/apps/mobile/src/components/agents/compute-status.ts +++ b/apps/mobile/src/components/agents/compute-status.ts @@ -1,4 +1,4 @@ -import { type Part } from 'cloud-agent-sdk'; +import { type Part } from '@kilocode/cloud-agent-sdk'; const toolStatusMap: Record = { read: 'Exploring', diff --git a/apps/mobile/src/components/agents/create-and-navigate-agent-session.test.ts b/apps/mobile/src/components/agents/create-and-navigate-agent-session.test.ts index 543a88a251..47b072d223 100644 --- a/apps/mobile/src/components/agents/create-and-navigate-agent-session.test.ts +++ b/apps/mobile/src/components/agents/create-and-navigate-agent-session.test.ts @@ -1,4 +1,4 @@ -import { type KiloSessionId } from 'cloud-agent-sdk'; +import { type KiloSessionId } from '@kilocode/cloud-agent-sdk'; import { describe, expect, it, vi } from 'vitest'; import { createAndNavigateAgentSession } from '@/components/agents/create-and-navigate-agent-session'; diff --git a/apps/mobile/src/components/agents/create-and-navigate-agent-session.ts b/apps/mobile/src/components/agents/create-and-navigate-agent-session.ts index 201e9e28bb..b52ed6e0b1 100644 --- a/apps/mobile/src/components/agents/create-and-navigate-agent-session.ts +++ b/apps/mobile/src/components/agents/create-and-navigate-agent-session.ts @@ -1,4 +1,4 @@ -import { type KiloSessionId } from 'cloud-agent-sdk'; +import { type KiloSessionId } from '@kilocode/cloud-agent-sdk'; import { createRemoteSessionWithFeedback } from '@/components/agents/create-remote-session-with-feedback'; import { replaceWithAgentSession } from '@/components/agents/session-detail-routes'; diff --git a/apps/mobile/src/components/agents/create-remote-session-with-feedback.test.ts b/apps/mobile/src/components/agents/create-remote-session-with-feedback.test.ts index d653625709..ddb78f1672 100644 --- a/apps/mobile/src/components/agents/create-remote-session-with-feedback.test.ts +++ b/apps/mobile/src/components/agents/create-remote-session-with-feedback.test.ts @@ -1,4 +1,4 @@ -import { type KiloSessionId } from 'cloud-agent-sdk'; +import { type KiloSessionId } from '@kilocode/cloud-agent-sdk'; import { describe, expect, it, vi } from 'vitest'; import { diff --git a/apps/mobile/src/components/agents/create-remote-session-with-feedback.ts b/apps/mobile/src/components/agents/create-remote-session-with-feedback.ts index d46cdf7980..27ec32f739 100644 --- a/apps/mobile/src/components/agents/create-remote-session-with-feedback.ts +++ b/apps/mobile/src/components/agents/create-remote-session-with-feedback.ts @@ -1,4 +1,4 @@ -import { type KiloSessionId } from 'cloud-agent-sdk'; +import { type KiloSessionId } from '@kilocode/cloud-agent-sdk'; export const CREATE_REMOTE_SESSION_FALLBACK_MESSAGE = 'Failed to create remote session'; diff --git a/apps/mobile/src/components/agents/file-part-renderer.tsx b/apps/mobile/src/components/agents/file-part-renderer.tsx index b7817a4ce6..57d9a56772 100644 --- a/apps/mobile/src/components/agents/file-part-renderer.tsx +++ b/apps/mobile/src/components/agents/file-part-renderer.tsx @@ -1,6 +1,6 @@ import { View } from 'react-native'; import { File as FileIcon } from 'lucide-react-native'; -import { type FilePart } from 'cloud-agent-sdk'; +import { type FilePart } from '@kilocode/cloud-agent-sdk'; import { Image } from '@/components/ui/image'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/message-bubble.test.ts b/apps/mobile/src/components/agents/message-bubble.test.ts index 91820bfa09..8765db6bc9 100644 --- a/apps/mobile/src/components/agents/message-bubble.test.ts +++ b/apps/mobile/src/components/agents/message-bubble.test.ts @@ -1,4 +1,4 @@ -import { type MessageDeliveryState, type StoredMessage } from 'cloud-agent-sdk'; +import { type MessageDeliveryState, type StoredMessage } from '@kilocode/cloud-agent-sdk'; import { describe, expect, it, vi } from 'vitest'; vi.mock('react-native', () => ({ diff --git a/apps/mobile/src/components/agents/message-bubble.tsx b/apps/mobile/src/components/agents/message-bubble.tsx index 6febcfa4b5..40e3740bc4 100644 --- a/apps/mobile/src/components/agents/message-bubble.tsx +++ b/apps/mobile/src/components/agents/message-bubble.tsx @@ -1,4 +1,4 @@ -import { type MessageDeliveryState, type StoredMessage } from 'cloud-agent-sdk'; +import { type MessageDeliveryState, type StoredMessage } from '@kilocode/cloud-agent-sdk'; import { Clock } from 'lucide-react-native'; import { type AccessibilityActionEvent, Pressable, View } from 'react-native'; import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; diff --git a/apps/mobile/src/components/agents/mobile-session-manager-helpers.ts b/apps/mobile/src/components/agents/mobile-session-manager-helpers.ts index 14f8f67938..05d49a4cad 100644 --- a/apps/mobile/src/components/agents/mobile-session-manager-helpers.ts +++ b/apps/mobile/src/components/agents/mobile-session-manager-helpers.ts @@ -1,4 +1,4 @@ -import { type RemoteAttachmentPart } from 'cloud-agent-sdk'; +import { type RemoteAttachmentPart } from '@kilocode/cloud-agent-sdk'; import { type AgentAttachmentSubmissionPayload } from '@/lib/agent-attachments/agent-attachment-types'; import { mimeForExtension, normalizeAttachmentExtension } from '@/lib/agent-attachments/validate'; diff --git a/apps/mobile/src/components/agents/mobile-session-manager.ts b/apps/mobile/src/components/agents/mobile-session-manager.ts index 4b2b380e2b..197bc30e64 100644 --- a/apps/mobile/src/components/agents/mobile-session-manager.ts +++ b/apps/mobile/src/components/agents/mobile-session-manager.ts @@ -10,7 +10,7 @@ import { type SessionManager, type SessionSnapshot, type UserWebConnection, -} from 'cloud-agent-sdk'; +} from '@kilocode/cloud-agent-sdk'; import { normalizeTransportPayload } from '@/components/agents/mobile-session-transport-payload'; import { formatSafeCloudAgentFailureDiagnostic, diff --git a/apps/mobile/src/components/agents/mobile-session-page-adapter.test.ts b/apps/mobile/src/components/agents/mobile-session-page-adapter.test.ts index e7f88d387b..2241f56940 100644 --- a/apps/mobile/src/components/agents/mobile-session-page-adapter.test.ts +++ b/apps/mobile/src/components/agents/mobile-session-page-adapter.test.ts @@ -5,7 +5,7 @@ import { type KiloSdkStoredMessage, type KiloSessionId, type SessionSnapshotPageOutcome, -} from 'cloud-agent-sdk'; +} from '@kilocode/cloud-agent-sdk'; const mocks = vi.hoisted(() => ({ getSessionMessagesPageQuery: vi.fn(), diff --git a/apps/mobile/src/components/agents/mobile-session-page-adapter.ts b/apps/mobile/src/components/agents/mobile-session-page-adapter.ts index 26ca54d13e..002ba646c8 100644 --- a/apps/mobile/src/components/agents/mobile-session-page-adapter.ts +++ b/apps/mobile/src/components/agents/mobile-session-page-adapter.ts @@ -4,7 +4,7 @@ import { type KiloSessionId, type SessionSnapshotPage, type SessionSnapshotPageOutcome, -} from 'cloud-agent-sdk'; +} from '@kilocode/cloud-agent-sdk'; import { trpcClient } from '@/lib/trpc'; /** diff --git a/apps/mobile/src/components/agents/mobile-session-transport-payload.ts b/apps/mobile/src/components/agents/mobile-session-transport-payload.ts index 32580a7c68..793942ff79 100644 --- a/apps/mobile/src/components/agents/mobile-session-transport-payload.ts +++ b/apps/mobile/src/components/agents/mobile-session-transport-payload.ts @@ -1,7 +1,6 @@ -import { type TransportSendPayload } from 'cloud-agent-sdk'; +import { type SendMessagePayload, type TransportSendPayload } from '@kilocode/cloud-agent-sdk'; import { normalizeAgentMode } from '@/components/agents/mode-options'; -import { type SendMessagePayload } from '@/lib/cloud-agent-next/types'; /** * Normalize a transport send payload into the wire `SendMessagePayload`. diff --git a/apps/mobile/src/components/agents/part-renderer.test.ts b/apps/mobile/src/components/agents/part-renderer.test.ts index d8e624ddaf..5284f82170 100644 --- a/apps/mobile/src/components/agents/part-renderer.test.ts +++ b/apps/mobile/src/components/agents/part-renderer.test.ts @@ -1,4 +1,4 @@ -import { type ReasoningPart } from 'cloud-agent-sdk'; +import { type ReasoningPart } from '@kilocode/cloud-agent-sdk'; import { describe, expect, it, vi } from 'vitest'; import { PartRenderer } from './part-renderer'; diff --git a/apps/mobile/src/components/agents/part-renderer.tsx b/apps/mobile/src/components/agents/part-renderer.tsx index d997fd5ce9..ec00676c2a 100644 --- a/apps/mobile/src/components/agents/part-renderer.tsx +++ b/apps/mobile/src/components/agents/part-renderer.tsx @@ -1,4 +1,4 @@ -import { type Part, type StoredMessage } from 'cloud-agent-sdk'; +import { type Part, type StoredMessage } from '@kilocode/cloud-agent-sdk'; import { CompactionSeparator } from './compaction-separator'; import { FilePartRenderer } from './file-part-renderer'; diff --git a/apps/mobile/src/components/agents/part-types.test.ts b/apps/mobile/src/components/agents/part-types.test.ts index f2b433c6bb..47a1c61650 100644 --- a/apps/mobile/src/components/agents/part-types.test.ts +++ b/apps/mobile/src/components/agents/part-types.test.ts @@ -1,4 +1,4 @@ -import { type Part, type ReasoningPart } from 'cloud-agent-sdk'; +import { type Part, type ReasoningPart } from '@kilocode/cloud-agent-sdk'; import { describe, expect, it } from 'vitest'; import { isPartStreaming, shouldRenderReasoningPart } from './part-types'; diff --git a/apps/mobile/src/components/agents/part-types.ts b/apps/mobile/src/components/agents/part-types.ts index 8976391469..463a0f56cd 100644 --- a/apps/mobile/src/components/agents/part-types.ts +++ b/apps/mobile/src/components/agents/part-types.ts @@ -5,7 +5,7 @@ import { type ReasoningPart, type TextPart, type ToolPart, -} from 'cloud-agent-sdk'; +} from '@kilocode/cloud-agent-sdk'; export function isTextPart(part: Part): part is TextPart { return part.type === 'text'; diff --git a/apps/mobile/src/components/agents/preparation-group.tsx b/apps/mobile/src/components/agents/preparation-group.tsx index ca75f82ff2..c43a3670f9 100644 --- a/apps/mobile/src/components/agents/preparation-group.tsx +++ b/apps/mobile/src/components/agents/preparation-group.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { ActivityIndicator, Pressable, View } from 'react-native'; import { AlertCircle, Check, ChevronRight, Terminal } from 'lucide-react-native'; -import { type PreparationAttempt, type PreparationStepSnapshot } from 'cloud-agent-sdk'; +import { type PreparationAttempt, type PreparationStepSnapshot } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; import { useThemeColors } from '@/lib/hooks/use-theme-colors'; diff --git a/apps/mobile/src/components/agents/session-detail-content.tsx b/apps/mobile/src/components/agents/session-detail-content.tsx index 3ce88b978c..3cc0f8b0eb 100644 --- a/apps/mobile/src/components/agents/session-detail-content.tsx +++ b/apps/mobile/src/components/agents/session-detail-content.tsx @@ -1,5 +1,5 @@ /* eslint-disable max-lines -- Session orchestration and its render paths are kept together. */ -import { type CloudStatus, type KiloSessionId } from 'cloud-agent-sdk'; +import { type CloudStatus, type KiloSessionId } from '@kilocode/cloud-agent-sdk'; import { type Href, useRouter } from 'expo-router'; import { useAtomValue } from 'jotai'; import { MessageSquare } from 'lucide-react-native'; diff --git a/apps/mobile/src/components/agents/session-detail-routes.test.ts b/apps/mobile/src/components/agents/session-detail-routes.test.ts index 1366d834ad..9429030204 100644 --- a/apps/mobile/src/components/agents/session-detail-routes.test.ts +++ b/apps/mobile/src/components/agents/session-detail-routes.test.ts @@ -1,4 +1,4 @@ -import { type KiloSessionId } from 'cloud-agent-sdk'; +import { type KiloSessionId } from '@kilocode/cloud-agent-sdk'; import { describe, expect, it, vi } from 'vitest'; import { diff --git a/apps/mobile/src/components/agents/session-detail-send-attachment.test.ts b/apps/mobile/src/components/agents/session-detail-send-attachment.test.ts index a72f81ead1..cd80684eb3 100644 --- a/apps/mobile/src/components/agents/session-detail-send-attachment.test.ts +++ b/apps/mobile/src/components/agents/session-detail-send-attachment.test.ts @@ -1,4 +1,4 @@ -import { type RemoteAttachmentPart } from 'cloud-agent-sdk'; +import { type RemoteAttachmentPart } from '@kilocode/cloud-agent-sdk'; import { describe, expect, it } from 'vitest'; import { buildRemoteAttachmentPartsWithRetryableFeedback } from './session-detail-send-attachment'; diff --git a/apps/mobile/src/components/agents/session-detail-send-attachment.ts b/apps/mobile/src/components/agents/session-detail-send-attachment.ts index 6a82d4b869..d0089fe80d 100644 --- a/apps/mobile/src/components/agents/session-detail-send-attachment.ts +++ b/apps/mobile/src/components/agents/session-detail-send-attachment.ts @@ -1,4 +1,4 @@ -import { type RemoteAttachmentPart, type ResolvedSession } from 'cloud-agent-sdk'; +import { type RemoteAttachmentPart, type ResolvedSession } from '@kilocode/cloud-agent-sdk'; import { type AgentAttachmentSubmissionPayload } from '@/lib/agent-attachments/agent-attachment-types'; diff --git a/apps/mobile/src/components/agents/session-message-list-state.test.ts b/apps/mobile/src/components/agents/session-message-list-state.test.ts index 0c8d8f0773..7cd6b2bd94 100644 --- a/apps/mobile/src/components/agents/session-message-list-state.test.ts +++ b/apps/mobile/src/components/agents/session-message-list-state.test.ts @@ -1,4 +1,4 @@ -import { type OlderMessagesError } from 'cloud-agent-sdk'; +import { type OlderMessagesError } from '@kilocode/cloud-agent-sdk'; import { describe, expect, it } from 'vitest'; import { selectSessionMessageListHeaderState, diff --git a/apps/mobile/src/components/agents/session-message-list-state.ts b/apps/mobile/src/components/agents/session-message-list-state.ts index cc1e8e93fe..84317bd4d9 100644 --- a/apps/mobile/src/components/agents/session-message-list-state.ts +++ b/apps/mobile/src/components/agents/session-message-list-state.ts @@ -1,4 +1,4 @@ -import { type OlderMessagesError } from 'cloud-agent-sdk'; +import { type OlderMessagesError } from '@kilocode/cloud-agent-sdk'; /** * Pagination header state for `SessionMessageList`. The component renders diff --git a/apps/mobile/src/components/agents/session-message-list.tsx b/apps/mobile/src/components/agents/session-message-list.tsx index 5dcf6ed40b..776110720c 100644 --- a/apps/mobile/src/components/agents/session-message-list.tsx +++ b/apps/mobile/src/components/agents/session-message-list.tsx @@ -1,5 +1,5 @@ import { FlashList, type FlashListRef, type ListRenderItem } from '@shopify/flash-list'; -import { type OlderMessagesError } from 'cloud-agent-sdk'; +import { type OlderMessagesError } from '@kilocode/cloud-agent-sdk'; import { ChevronDown } from 'lucide-react-native'; import { useCallback, useEffect, useRef } from 'react'; import { Pressable, View, type ViewStyle } from 'react-native'; diff --git a/apps/mobile/src/components/agents/session-pagination-header-render-model.test.ts b/apps/mobile/src/components/agents/session-pagination-header-render-model.test.ts index 5282f50bc1..9408f93cf4 100644 --- a/apps/mobile/src/components/agents/session-pagination-header-render-model.test.ts +++ b/apps/mobile/src/components/agents/session-pagination-header-render-model.test.ts @@ -1,4 +1,4 @@ -import { type OlderMessagesError } from 'cloud-agent-sdk'; +import { type OlderMessagesError } from '@kilocode/cloud-agent-sdk'; import { describe, expect, it } from 'vitest'; import { selectSessionPaginationHeaderRenderModel } from '@/components/agents/session-pagination-header-render-model'; import { type SessionMessageListHeaderStateInputs } from '@/components/agents/session-message-list-state'; diff --git a/apps/mobile/src/components/agents/session-provider.tsx b/apps/mobile/src/components/agents/session-provider.tsx index fcdd03c3e3..8cae40abae 100644 --- a/apps/mobile/src/components/agents/session-provider.tsx +++ b/apps/mobile/src/components/agents/session-provider.tsx @@ -1,6 +1,6 @@ import { createContext, type ReactNode, useContext, useEffect, useRef } from 'react'; import { createStore, Provider as JotaiProvider } from 'jotai'; -import { type SessionManager } from 'cloud-agent-sdk'; +import { type SessionManager } from '@kilocode/cloud-agent-sdk'; import { createMobileAgentSessionManager } from '@/components/agents/mobile-session-manager'; import { useUserWebConnection } from '@/components/agents/user-web-connection-provider'; diff --git a/apps/mobile/src/components/agents/session-status-indicator.tsx b/apps/mobile/src/components/agents/session-status-indicator.tsx index 1ca5e3b1ec..fc8ead6050 100644 --- a/apps/mobile/src/components/agents/session-status-indicator.tsx +++ b/apps/mobile/src/components/agents/session-status-indicator.tsx @@ -1,6 +1,6 @@ import { ActivityIndicator, View } from 'react-native'; import { AlertCircle, Check } from 'lucide-react-native'; -import { type SessionStatusIndicator as SessionStatusIndicatorType } from 'cloud-agent-sdk'; +import { type SessionStatusIndicator as SessionStatusIndicatorType } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; import { useThemeColors } from '@/lib/hooks/use-theme-colors'; diff --git a/apps/mobile/src/components/agents/session-transcript.ts b/apps/mobile/src/components/agents/session-transcript.ts index b9d46c03cf..045071139a 100644 --- a/apps/mobile/src/components/agents/session-transcript.ts +++ b/apps/mobile/src/components/agents/session-transcript.ts @@ -1,5 +1,5 @@ -import { isNoOpCompletedPreparationAttempt } from 'cloud-agent-sdk/preparation-attempts'; -import { type PreparationAttempt, type StoredMessage } from 'cloud-agent-sdk'; +import { isNoOpCompletedPreparationAttempt } from '@kilocode/cloud-agent-sdk/preparation-attempts'; +import { type PreparationAttempt, type StoredMessage } from '@kilocode/cloud-agent-sdk'; export type SessionTranscriptItem = | { type: 'message'; message: StoredMessage } diff --git a/apps/mobile/src/components/agents/slash-command-suggestions.tsx b/apps/mobile/src/components/agents/slash-command-suggestions.tsx index 8a21a3308f..5cb60bb19a 100644 --- a/apps/mobile/src/components/agents/slash-command-suggestions.tsx +++ b/apps/mobile/src/components/agents/slash-command-suggestions.tsx @@ -1,4 +1,4 @@ -import { type SlashCommandInfo } from 'cloud-agent-sdk'; +import { type SlashCommandInfo } from '@kilocode/cloud-agent-sdk'; import { Pressable, ScrollView, View } from 'react-native'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/suggest-tool-card.tsx b/apps/mobile/src/components/agents/suggest-tool-card.tsx index ed9a2b0a1c..b971502821 100644 --- a/apps/mobile/src/components/agents/suggest-tool-card.tsx +++ b/apps/mobile/src/components/agents/suggest-tool-card.tsx @@ -1,6 +1,6 @@ import { useAtomValue } from 'jotai'; import { Sparkles } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { useSessionManager } from '@/components/agents/session-provider'; diff --git a/apps/mobile/src/components/agents/suggestion-card.tsx b/apps/mobile/src/components/agents/suggestion-card.tsx index 38f0065d4d..1d9747b854 100644 --- a/apps/mobile/src/components/agents/suggestion-card.tsx +++ b/apps/mobile/src/components/agents/suggestion-card.tsx @@ -2,7 +2,7 @@ import { useRef, useState } from 'react'; import { View } from 'react-native'; import * as Haptics from 'expo-haptics'; import { Sparkles } from 'lucide-react-native'; -import { type StandaloneSuggestion, type SuggestionAction } from 'cloud-agent-sdk'; +import { type StandaloneSuggestion, type SuggestionAction } from '@kilocode/cloud-agent-sdk'; import { Button } from '@/components/ui/button'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-cards/bash-tool-card.tsx b/apps/mobile/src/components/agents/tool-cards/bash-tool-card.tsx index f81e1e51d6..78f9de4fb4 100644 --- a/apps/mobile/src/components/agents/tool-cards/bash-tool-card.tsx +++ b/apps/mobile/src/components/agents/tool-cards/bash-tool-card.tsx @@ -1,6 +1,6 @@ import { ScrollView, View } from 'react-native'; import { Terminal } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-cards/edit-tool-card.tsx b/apps/mobile/src/components/agents/tool-cards/edit-tool-card.tsx index 2a0b34de60..91d93b8062 100644 --- a/apps/mobile/src/components/agents/tool-cards/edit-tool-card.tsx +++ b/apps/mobile/src/components/agents/tool-cards/edit-tool-card.tsx @@ -1,6 +1,6 @@ import { ScrollView, View } from 'react-native'; import { Pencil } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-cards/generic-tool-card.tsx b/apps/mobile/src/components/agents/tool-cards/generic-tool-card.tsx index 892f13a7bf..479392553c 100644 --- a/apps/mobile/src/components/agents/tool-cards/generic-tool-card.tsx +++ b/apps/mobile/src/components/agents/tool-cards/generic-tool-card.tsx @@ -1,6 +1,6 @@ import { ScrollView, View } from 'react-native'; import { Plug } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-cards/glob-tool-card.tsx b/apps/mobile/src/components/agents/tool-cards/glob-tool-card.tsx index 5dcba263e8..acfdb6c88c 100644 --- a/apps/mobile/src/components/agents/tool-cards/glob-tool-card.tsx +++ b/apps/mobile/src/components/agents/tool-cards/glob-tool-card.tsx @@ -1,6 +1,6 @@ import { ScrollView } from 'react-native'; import { Search } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-cards/grep-tool-card.tsx b/apps/mobile/src/components/agents/tool-cards/grep-tool-card.tsx index db19da1b23..a48d8ec0dc 100644 --- a/apps/mobile/src/components/agents/tool-cards/grep-tool-card.tsx +++ b/apps/mobile/src/components/agents/tool-cards/grep-tool-card.tsx @@ -1,6 +1,6 @@ import { ScrollView } from 'react-native'; import { FileSearch } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-cards/list-tool-card.tsx b/apps/mobile/src/components/agents/tool-cards/list-tool-card.tsx index 1635883d80..e36a69ab07 100644 --- a/apps/mobile/src/components/agents/tool-cards/list-tool-card.tsx +++ b/apps/mobile/src/components/agents/tool-cards/list-tool-card.tsx @@ -1,6 +1,6 @@ import { ScrollView } from 'react-native'; import { FolderOpen } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-cards/read-tool-card.tsx b/apps/mobile/src/components/agents/tool-cards/read-tool-card.tsx index 19684f2558..f18ac1e701 100644 --- a/apps/mobile/src/components/agents/tool-cards/read-tool-card.tsx +++ b/apps/mobile/src/components/agents/tool-cards/read-tool-card.tsx @@ -1,6 +1,6 @@ import { ScrollView } from 'react-native'; import { Eye } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-cards/task-tool-card.tsx b/apps/mobile/src/components/agents/tool-cards/task-tool-card.tsx index f7d91ff949..4b086bdbe9 100644 --- a/apps/mobile/src/components/agents/tool-cards/task-tool-card.tsx +++ b/apps/mobile/src/components/agents/tool-cards/task-tool-card.tsx @@ -1,6 +1,6 @@ import { ScrollView } from 'react-native'; import { Cpu } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-cards/todo-tool-card.tsx b/apps/mobile/src/components/agents/tool-cards/todo-tool-card.tsx index 1ad01ed034..86356317c2 100644 --- a/apps/mobile/src/components/agents/tool-cards/todo-tool-card.tsx +++ b/apps/mobile/src/components/agents/tool-cards/todo-tool-card.tsx @@ -1,6 +1,6 @@ import { ScrollView } from 'react-native'; import { ListTodo } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-cards/web-search-tool-card.tsx b/apps/mobile/src/components/agents/tool-cards/web-search-tool-card.tsx index 13f835fb89..89f3c7bd65 100644 --- a/apps/mobile/src/components/agents/tool-cards/web-search-tool-card.tsx +++ b/apps/mobile/src/components/agents/tool-cards/web-search-tool-card.tsx @@ -1,6 +1,6 @@ import { ScrollView } from 'react-native'; import { Globe } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-cards/write-tool-card.tsx b/apps/mobile/src/components/agents/tool-cards/write-tool-card.tsx index 771830e831..7f5020399d 100644 --- a/apps/mobile/src/components/agents/tool-cards/write-tool-card.tsx +++ b/apps/mobile/src/components/agents/tool-cards/write-tool-card.tsx @@ -1,6 +1,6 @@ import { ScrollView } from 'react-native'; import { FilePlus } from 'lucide-react-native'; -import { type ToolPart } from 'cloud-agent-sdk'; +import { type ToolPart } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; diff --git a/apps/mobile/src/components/agents/tool-part-renderer.tsx b/apps/mobile/src/components/agents/tool-part-renderer.tsx index d17e7c3a72..c4c626fd35 100644 --- a/apps/mobile/src/components/agents/tool-part-renderer.tsx +++ b/apps/mobile/src/components/agents/tool-part-renderer.tsx @@ -1,4 +1,4 @@ -import { type StoredMessage, type ToolPart } from 'cloud-agent-sdk'; +import { type StoredMessage, type ToolPart } from '@kilocode/cloud-agent-sdk'; import { ChildSessionSection, diff --git a/apps/mobile/src/components/agents/use-message-copy.ts b/apps/mobile/src/components/agents/use-message-copy.ts index f258ef59aa..ee676d5c23 100644 --- a/apps/mobile/src/components/agents/use-message-copy.ts +++ b/apps/mobile/src/components/agents/use-message-copy.ts @@ -1,4 +1,4 @@ -import { type StoredMessage } from 'cloud-agent-sdk'; +import { type StoredMessage } from '@kilocode/cloud-agent-sdk'; import * as Clipboard from 'expo-clipboard'; import * as Haptics from 'expo-haptics'; import { useCallback } from 'react'; diff --git a/apps/mobile/src/components/agents/use-new-session-creator.ts b/apps/mobile/src/components/agents/use-new-session-creator.ts index c9f5a47654..4d07ef8006 100644 --- a/apps/mobile/src/components/agents/use-new-session-creator.ts +++ b/apps/mobile/src/components/agents/use-new-session-creator.ts @@ -1,7 +1,7 @@ import { type RefObject, useCallback, useRef } from 'react'; import { type Href, useNavigation, useRouter } from 'expo-router'; import { useQueryClient } from '@tanstack/react-query'; -import { generateMessageId } from 'cloud-agent-sdk/message-id'; +import { generateMessageId } from '@kilocode/cloud-agent-sdk/message-id'; import * as Haptics from 'expo-haptics'; import { toast } from 'sonner-native'; diff --git a/apps/mobile/src/components/agents/use-session-config-sync.ts b/apps/mobile/src/components/agents/use-session-config-sync.ts index 7172e16e97..a202cff76a 100644 --- a/apps/mobile/src/components/agents/use-session-config-sync.ts +++ b/apps/mobile/src/components/agents/use-session-config-sync.ts @@ -1,4 +1,4 @@ -import { type ResolvedSession } from 'cloud-agent-sdk'; +import { type ResolvedSession } from '@kilocode/cloud-agent-sdk'; import { useEffect, useState } from 'react'; import { normalizeAgentMode } from '@/components/agents/mode-options'; diff --git a/apps/mobile/src/components/agents/use-session-detail-rename.ts b/apps/mobile/src/components/agents/use-session-detail-rename.ts index 8bde6cfd19..639df57f3b 100644 --- a/apps/mobile/src/components/agents/use-session-detail-rename.ts +++ b/apps/mobile/src/components/agents/use-session-detail-rename.ts @@ -1,4 +1,4 @@ -import { type KiloSessionId } from 'cloud-agent-sdk'; +import { type KiloSessionId } from '@kilocode/cloud-agent-sdk'; import { useCallback, useEffect, useReducer, useRef } from 'react'; import { useSessionMutations } from '@/lib/hooks/use-session-mutations'; diff --git a/apps/mobile/src/components/agents/user-web-connection-provider.tsx b/apps/mobile/src/components/agents/user-web-connection-provider.tsx index f718a2bb09..48c9fa28f6 100644 --- a/apps/mobile/src/components/agents/user-web-connection-provider.tsx +++ b/apps/mobile/src/components/agents/user-web-connection-provider.tsx @@ -1,5 +1,5 @@ import { createContext, type ReactNode, useContext, useEffect, useRef } from 'react'; -import { type UserWebConnection } from 'cloud-agent-sdk'; +import { type UserWebConnection } from '@kilocode/cloud-agent-sdk'; // kilocode_change - K1/C2: `createUserWebConnection` must come from its // narrow subpath, not the `cloud-agent-sdk` barrel. The barrel's index.ts // also re-exports web-only transport code that imports a web-app `@/...` @@ -8,7 +8,7 @@ import { type UserWebConnection } from 'cloud-agent-sdk'; // actually imported this provider (and thus the barrel) at runtime until // the `kilo remote` spawn hook test did. See the matching // vitest.config.ts aliases for the full explanation. -import { createUserWebConnection } from 'cloud-agent-sdk/user-web-connection'; +import { createUserWebConnection } from '@kilocode/cloud-agent-sdk/user-web-connection'; import { SESSION_INGEST_WS_URL } from '@/lib/config'; import { createNativeUserWebConnectionLifecycleHooks } from '@/lib/user-web-connection-lifecycle'; diff --git a/apps/mobile/src/components/agents/working-indicator.tsx b/apps/mobile/src/components/agents/working-indicator.tsx index c9b3815f1e..7e85dba1a7 100644 --- a/apps/mobile/src/components/agents/working-indicator.tsx +++ b/apps/mobile/src/components/agents/working-indicator.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react'; import { ActivityIndicator } from 'react-native'; import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'; -import { type StoredMessage } from 'cloud-agent-sdk'; +import { type StoredMessage } from '@kilocode/cloud-agent-sdk'; import { Text } from '@/components/ui/text'; import { useThemeColors } from '@/lib/hooks/use-theme-colors'; diff --git a/apps/mobile/src/lib/active-sessions-live-sync.test-helpers.ts b/apps/mobile/src/lib/active-sessions-live-sync.test-helpers.ts index 742773d94b..bb26c30bd2 100644 --- a/apps/mobile/src/lib/active-sessions-live-sync.test-helpers.ts +++ b/apps/mobile/src/lib/active-sessions-live-sync.test-helpers.ts @@ -10,7 +10,7 @@ import { type CachedActiveSession, type CachedActiveSessionsData, } from '@/lib/active-sessions-live'; -import { type UserWebSystemEvent } from 'cloud-agent-sdk'; +import { type UserWebSystemEvent } from '@kilocode/cloud-agent-sdk'; export type { CachedActiveSessionsData }; diff --git a/apps/mobile/src/lib/active-sessions-live-sync.ts b/apps/mobile/src/lib/active-sessions-live-sync.ts index d235d40ca6..67ee10c43c 100644 --- a/apps/mobile/src/lib/active-sessions-live-sync.ts +++ b/apps/mobile/src/lib/active-sessions-live-sync.ts @@ -35,7 +35,7 @@ import { selectRootWsSessions, } from './active-sessions-live'; -import { type UserWebConnection, type UserWebSystemEvent } from 'cloud-agent-sdk'; +import { type UserWebConnection, type UserWebSystemEvent } from '@kilocode/cloud-agent-sdk'; const ENRICHMENT_RETRY_MIN_INTERVAL_MS = 10_000; diff --git a/apps/mobile/src/lib/active-sessions-live.ts b/apps/mobile/src/lib/active-sessions-live.ts index 043eda556e..b494582ce3 100644 --- a/apps/mobile/src/lib/active-sessions-live.ts +++ b/apps/mobile/src/lib/active-sessions-live.ts @@ -17,7 +17,7 @@ import { heartbeatDataSchema, type SessionsListData, sessionsListDataSchema, -} from 'cloud-agent-sdk/schemas'; +} from '@kilocode/cloud-agent-sdk/schemas'; import { type ActiveSession } from '@/lib/hooks/use-agent-sessions'; diff --git a/apps/mobile/src/lib/cloud-agent-runtime.ts b/apps/mobile/src/lib/cloud-agent-runtime.ts index 0365340752..7cc2d70aee 100644 --- a/apps/mobile/src/lib/cloud-agent-runtime.ts +++ b/apps/mobile/src/lib/cloud-agent-runtime.ts @@ -1,5 +1,5 @@ import * as Crypto from 'expo-crypto'; -import { configureCloudAgentSdkRuntime } from 'cloud-agent-sdk'; +import { configureCloudAgentSdkRuntime } from '@kilocode/cloud-agent-sdk'; configureCloudAgentSdkRuntime({ randomBytes: Crypto.getRandomBytes, diff --git a/apps/mobile/src/lib/hooks/remote-instance-spawn-classifier.ts b/apps/mobile/src/lib/hooks/remote-instance-spawn-classifier.ts index 63452a5b6f..4a2fe726e2 100644 --- a/apps/mobile/src/lib/hooks/remote-instance-spawn-classifier.ts +++ b/apps/mobile/src/lib/hooks/remote-instance-spawn-classifier.ts @@ -1,4 +1,4 @@ -import { type KiloSessionId, type UserWebConnection } from 'cloud-agent-sdk'; +import { type KiloSessionId, type UserWebConnection } from '@kilocode/cloud-agent-sdk'; // kilocode_change - K1/C2: these two runtime imports must come from their // narrow subpaths, not the `cloud-agent-sdk` barrel. The barrel's index.ts // also re-exports web-only transport code (`cloud-agent-connection.ts` -> @@ -16,11 +16,14 @@ import { type KiloSessionId, type UserWebConnection } from 'cloud-agent-sdk'; // syntax the Node vitest environment cannot parse. Splitting keeps this // file's pure functions testable "without a React renderer" (per the // accepted plan) while the hook itself stays UI-only and untested here. -import { CommandDeliveredError, UserWebCommandError } from 'cloud-agent-sdk/user-web-connection'; +import { + CommandDeliveredError, + UserWebCommandError, +} from '@kilocode/cloud-agent-sdk/user-web-connection'; import { createRemoteSessionOnConnection, parseCreateSessionResponse, -} from 'cloud-agent-sdk/create-session'; +} from '@kilocode/cloud-agent-sdk/create-session'; /** * Pure outcome classifier for the `create_session` reply (connection-scoped diff --git a/apps/mobile/src/lib/hooks/use-remote-instance-spawn.test.ts b/apps/mobile/src/lib/hooks/use-remote-instance-spawn.test.ts index 10f9de3b87..81bedb0cf2 100644 --- a/apps/mobile/src/lib/hooks/use-remote-instance-spawn.test.ts +++ b/apps/mobile/src/lib/hooks/use-remote-instance-spawn.test.ts @@ -1,10 +1,13 @@ import { describe, expect, it } from 'vitest'; -import { type KiloSessionId, type UserWebConnection } from 'cloud-agent-sdk'; +import { type KiloSessionId, type UserWebConnection } from '@kilocode/cloud-agent-sdk'; // kilocode_change - K1/C2: runtime imports via the narrow subpath alias — // see the matching comment in remote-instance-spawn-classifier.ts and // vitest.config.ts for why the full `cloud-agent-sdk` barrel is unsafe here. -import { CommandDeliveredError, UserWebCommandError } from 'cloud-agent-sdk/user-web-connection'; +import { + CommandDeliveredError, + UserWebCommandError, +} from '@kilocode/cloud-agent-sdk/user-web-connection'; // kilocode_change - K1/C2: imported from the classifier module, not // `use-remote-instance-spawn.ts` — that file's `useRemoteInstanceSpawn` hook diff --git a/apps/mobile/src/lib/hooks/use-remote-instance-spawn.ts b/apps/mobile/src/lib/hooks/use-remote-instance-spawn.ts index d965d87471..aace75edac 100644 --- a/apps/mobile/src/lib/hooks/use-remote-instance-spawn.ts +++ b/apps/mobile/src/lib/hooks/use-remote-instance-spawn.ts @@ -1,5 +1,5 @@ import { useMemo, useState } from 'react'; -import { type KiloSessionId } from 'cloud-agent-sdk'; +import { type KiloSessionId } from '@kilocode/cloud-agent-sdk'; import { useUserWebConnection } from '@/components/agents/user-web-connection-provider'; diff --git a/apps/mobile/src/lib/hooks/use-session-model-options.ts b/apps/mobile/src/lib/hooks/use-session-model-options.ts index 2a9284d596..a5f7941f7b 100644 --- a/apps/mobile/src/lib/hooks/use-session-model-options.ts +++ b/apps/mobile/src/lib/hooks/use-session-model-options.ts @@ -6,8 +6,8 @@ import { type RemoteModelOverride, type RemoteModelState, type ResolvedSession, -} from 'cloud-agent-sdk'; -import { sortRemoteModelCatalogProviders } from 'cloud-agent-sdk/remote-model-order'; +} from '@kilocode/cloud-agent-sdk'; +import { sortRemoteModelCatalogProviders } from '@kilocode/cloud-agent-sdk/remote-model-order'; import { type ModelOption } from '@/lib/hooks/use-available-models'; diff --git a/apps/mobile/src/lib/remote-submit-outcome.test.ts b/apps/mobile/src/lib/remote-submit-outcome.test.ts index ccd0672245..e47412f8aa 100644 --- a/apps/mobile/src/lib/remote-submit-outcome.test.ts +++ b/apps/mobile/src/lib/remote-submit-outcome.test.ts @@ -1,4 +1,4 @@ -import { type KiloSessionId } from 'cloud-agent-sdk'; +import { type KiloSessionId } from '@kilocode/cloud-agent-sdk'; import { describe, expect, it } from 'vitest'; import { type InstancePickerInstance } from '@/lib/picker-bridge'; diff --git a/apps/mobile/src/lib/remote-submit-outcome.ts b/apps/mobile/src/lib/remote-submit-outcome.ts index 4edca0d927..585c24385f 100644 --- a/apps/mobile/src/lib/remote-submit-outcome.ts +++ b/apps/mobile/src/lib/remote-submit-outcome.ts @@ -1,4 +1,4 @@ -import { type KiloSessionId } from 'cloud-agent-sdk'; +import { type KiloSessionId } from '@kilocode/cloud-agent-sdk'; import { type InstancePickerInstance } from '@/lib/picker-bridge'; import { type CreateSessionOutcome } from '@/lib/hooks/use-remote-instance-spawn'; diff --git a/apps/mobile/src/lib/session-context-info.test.ts b/apps/mobile/src/lib/session-context-info.test.ts index 5c35ea3f2a..4c9ee884c1 100644 --- a/apps/mobile/src/lib/session-context-info.test.ts +++ b/apps/mobile/src/lib/session-context-info.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { type ContextUsage } from 'cloud-agent-sdk/context-usage'; +import { type ContextUsage } from '@kilocode/cloud-agent-sdk/context-usage'; import { type SessionModelOption } from './hooks/use-session-model-options'; import { resolveSessionContextInfo } from './session-context-info'; diff --git a/apps/mobile/src/lib/session-context-info.ts b/apps/mobile/src/lib/session-context-info.ts index 4433f0e94f..2db3f4c1fd 100644 --- a/apps/mobile/src/lib/session-context-info.ts +++ b/apps/mobile/src/lib/session-context-info.ts @@ -1,4 +1,7 @@ -import { calculateContextUsagePercentage, type ContextUsage } from 'cloud-agent-sdk/context-usage'; +import { + calculateContextUsagePercentage, + type ContextUsage, +} from '@kilocode/cloud-agent-sdk/context-usage'; import { type SessionModelOption } from './hooks/use-session-model-options'; diff --git a/apps/mobile/src/lib/use-session-model-options.test.ts b/apps/mobile/src/lib/use-session-model-options.test.ts index 8a6ba17c04..ef44eabdcc 100644 --- a/apps/mobile/src/lib/use-session-model-options.test.ts +++ b/apps/mobile/src/lib/use-session-model-options.test.ts @@ -1,6 +1,6 @@ /* eslint-disable max-lines -- Model option tests mirror the SDK/web suite. */ import { describe, expect, it } from 'vitest'; -import { type ContextUsage } from 'cloud-agent-sdk/context-usage'; +import { type ContextUsage } from '@kilocode/cloud-agent-sdk/context-usage'; import { buildSessionModelOptions, diff --git a/apps/mobile/src/lib/user-web-connection-lifecycle.ts b/apps/mobile/src/lib/user-web-connection-lifecycle.ts index 3c8d69de42..4f7bfae1f2 100644 --- a/apps/mobile/src/lib/user-web-connection-lifecycle.ts +++ b/apps/mobile/src/lib/user-web-connection-lifecycle.ts @@ -1,6 +1,6 @@ import { AppState, type AppStateStatus } from 'react-native'; import { addEventListener, type NetInfoState } from '@react-native-community/netinfo'; -import { type ConnectionLifecycleHooks } from 'cloud-agent-sdk'; +import { type ConnectionLifecycleHooks } from '@kilocode/cloud-agent-sdk'; type ConnectivityState = Pick; diff --git a/apps/mobile/tsconfig.json b/apps/mobile/tsconfig.json index 8fb0a18d2b..87c03988c8 100644 --- a/apps/mobile/tsconfig.json +++ b/apps/mobile/tsconfig.json @@ -4,14 +4,8 @@ "strict": true, "noUncheckedIndexedAccess": true, "paths": { - "@/types/*": ["../../apps/web/src/types/*"], - "@/lib/cloud-agent-next/*": ["../../apps/web/src/lib/cloud-agent-next/*"], - "@/lib/cloud-agent/constants": ["../../apps/web/src/lib/cloud-agent/constants.ts"], - "@/lib/images-schema": ["../../apps/web/src/lib/images-schema.ts"], "@/*": ["./src/*"], - "@/assets/*": ["./assets/*"], - "cloud-agent-sdk": ["../../apps/web/src/lib/cloud-agent-sdk/index.ts"], - "cloud-agent-sdk/*": ["../../apps/web/src/lib/cloud-agent-sdk/*"] + "@/assets/*": ["./assets/*"] } }, "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts", "nativewind-env.d.ts"] diff --git a/apps/mobile/vitest.config.ts b/apps/mobile/vitest.config.ts index 956954bcc5..eaaffc962c 100644 --- a/apps/mobile/vitest.config.ts +++ b/apps/mobile/vitest.config.ts @@ -6,45 +6,6 @@ export default defineConfig({ resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)), - 'cloud-agent-sdk/message-id': fileURLToPath( - new URL('../../apps/web/src/lib/cloud-agent-sdk/message-id.ts', import.meta.url) - ), - 'cloud-agent-sdk/context-usage': fileURLToPath( - new URL('../../apps/web/src/lib/cloud-agent-sdk/context-usage.ts', import.meta.url) - ), - 'cloud-agent-sdk/cli-model': fileURLToPath( - new URL('../../apps/web/src/lib/cloud-agent-sdk/cli-model.ts', import.meta.url) - ), - 'cloud-agent-sdk/remote-model-order': fileURLToPath( - new URL('../../apps/web/src/lib/cloud-agent-sdk/remote-model-order.ts', import.meta.url) - ), - 'cloud-agent-sdk/remote-command-catalog': fileURLToPath( - new URL('../../apps/web/src/lib/cloud-agent-sdk/remote-command-catalog.ts', import.meta.url) - ), - // kilocode_change - K1/C2: narrow subpaths for the `kilo remote` spawn - // hook. These two files have a self-contained import graph (schemas, - // base-connection, runtime, types — no `@/...` web-app-alias imports), - // unlike the full barrel (`cloud-agent-sdk` below), which transitively - // pulls in `cloud-agent-connection.ts` -> `cloud-agent-transport.ts` -> - // `@/lib/cloud-agent-next/event-types`, a web-only `@` alias that does - // not resolve under the mobile app's own `@` alias. Runtime imports of - // `CommandDeliveredError`/`UserWebCommandError`/`createRemoteSessionOnConnection` - // must go through these subpaths, not the barrel. - 'cloud-agent-sdk/user-web-connection': fileURLToPath( - new URL('../../apps/web/src/lib/cloud-agent-sdk/user-web-connection.ts', import.meta.url) - ), - 'cloud-agent-sdk/create-session': fileURLToPath( - new URL('../../apps/web/src/lib/cloud-agent-sdk/create-session.ts', import.meta.url) - ), - 'cloud-agent-sdk/preparation-attempts': fileURLToPath( - new URL('../../apps/web/src/lib/cloud-agent-sdk/preparation-attempts.ts', import.meta.url) - ), - 'cloud-agent-sdk/schemas': fileURLToPath( - new URL('../../apps/web/src/lib/cloud-agent-sdk/schemas.ts', import.meta.url) - ), - 'cloud-agent-sdk': fileURLToPath( - new URL('../../apps/web/src/lib/cloud-agent-sdk/index.ts', import.meta.url) - ), }, }, test: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68fb9ea6d9..417b31f72d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -272,6 +272,9 @@ importers: '@kilocode/app-shared': specifier: workspace:* version: link:../../packages/app-shared + '@kilocode/cloud-agent-sdk': + specifier: workspace:* + version: link:../../packages/cloud-agent-sdk '@kilocode/event-service': specifier: workspace:* version: link:../../packages/event-service From 266117d9f338a50f85fbb8f392c142b47e10289d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 23 Jul 2026 06:29:30 +0200 Subject: [PATCH 05/14] feat(trpc): expose client-safe MobileRouter and migrate mobile consumers --- .../src/lib/hooks/instance-context-logic.ts | 4 +- .../src/lib/hooks/use-agent-sessions.ts | 4 +- .../src/lib/hooks/use-kiloclaw-queries.ts | 4 +- .../src/lib/hooks/use-model-preferences.ts | 4 +- .../src/lib/kilo-pass/store-products.ts | 4 +- .../discussion/review-discussion-types.ts | 4 +- apps/mobile/src/lib/security-agent.ts | 10 +- apps/mobile/src/lib/session-list-cache.ts | 4 +- apps/mobile/src/lib/trpc.ts | 6 +- packages/trpc/package.json | 3 + packages/trpc/rollup.config.mjs | 92 +++++++++++-------- packages/trpc/src/mobile.ts | 44 +++++++++ 12 files changed, 124 insertions(+), 59 deletions(-) create mode 100644 packages/trpc/src/mobile.ts diff --git a/apps/mobile/src/lib/hooks/instance-context-logic.ts b/apps/mobile/src/lib/hooks/instance-context-logic.ts index 52a53d2197..d6c77d9f66 100644 --- a/apps/mobile/src/lib/hooks/instance-context-logic.ts +++ b/apps/mobile/src/lib/hooks/instance-context-logic.ts @@ -1,6 +1,6 @@ -import { type inferRouterOutputs, type RootRouter } from '@kilocode/trpc'; +import { type inferRouterOutputs, type MobileRouter } from '@kilocode/trpc/mobile'; -export type ClawInstance = inferRouterOutputs['kiloclaw']['listAllInstances'][number]; +export type ClawInstance = inferRouterOutputs['kiloclaw']['listAllInstances'][number]; export type InstanceContextResult = | { status: 'loading' } diff --git a/apps/mobile/src/lib/hooks/use-agent-sessions.ts b/apps/mobile/src/lib/hooks/use-agent-sessions.ts index 41714c52cf..f3f23c35cd 100644 --- a/apps/mobile/src/lib/hooks/use-agent-sessions.ts +++ b/apps/mobile/src/lib/hooks/use-agent-sessions.ts @@ -1,4 +1,4 @@ -import { type inferRouterOutputs, type RootRouter } from '@kilocode/trpc'; +import { type inferRouterOutputs, type MobileRouter } from '@kilocode/trpc/mobile'; import { keepPreviousData, useInfiniteQuery, useQuery } from '@tanstack/react-query'; import { useEffect, useMemo, useRef } from 'react'; @@ -17,7 +17,7 @@ import { useUserWebConnectionState } from '@/lib/hooks/use-user-web-connection-s // ── Types ──────────────────────────────────────────────────────────── -type RouterOutputs = inferRouterOutputs; +type RouterOutputs = inferRouterOutputs; export type StoredSession = RouterOutputs['cliSessionsV2']['list']['cliSessions'][number]; diff --git a/apps/mobile/src/lib/hooks/use-kiloclaw-queries.ts b/apps/mobile/src/lib/hooks/use-kiloclaw-queries.ts index 4eb64560ef..b205103190 100644 --- a/apps/mobile/src/lib/hooks/use-kiloclaw-queries.ts +++ b/apps/mobile/src/lib/hooks/use-kiloclaw-queries.ts @@ -1,4 +1,4 @@ -import { type inferRouterOutputs, type RootRouter } from '@kilocode/trpc'; +import { type inferRouterOutputs, type MobileRouter } from '@kilocode/trpc/mobile'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; import { useMemo } from 'react'; @@ -48,7 +48,7 @@ export function useKiloClawStatusQueryKey(organizationId: string | null) { : trpc.kiloclaw.getStatus.queryKey(); } -type BillingData = inferRouterOutputs['kiloclaw']['getBillingStatus']; +type BillingData = inferRouterOutputs['kiloclaw']['getBillingStatus']; type BillingPollDecider = (data: BillingData | undefined) => number; export function useKiloClawBillingStatus( diff --git a/apps/mobile/src/lib/hooks/use-model-preferences.ts b/apps/mobile/src/lib/hooks/use-model-preferences.ts index ef1793f6f6..cc5eb34019 100644 --- a/apps/mobile/src/lib/hooks/use-model-preferences.ts +++ b/apps/mobile/src/lib/hooks/use-model-preferences.ts @@ -1,12 +1,12 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { type inferRouterOutputs, type RootRouter } from '@kilocode/trpc'; +import { type inferRouterOutputs, type MobileRouter } from '@kilocode/trpc/mobile'; import { useCallback, useMemo, useState } from 'react'; import { toast } from 'sonner-native'; import { chainSave } from '@/lib/hooks/save-chain'; import { trpcClient, useTRPC } from '@/lib/trpc'; -type ModelPreferences = inferRouterOutputs['modelPreferences']['get']; +type ModelPreferences = inferRouterOutputs['modelPreferences']['get']; const onError = (error: { message: string }) => { toast.error(error.message || 'Something went wrong'); diff --git a/apps/mobile/src/lib/kilo-pass/store-products.ts b/apps/mobile/src/lib/kilo-pass/store-products.ts index abf307e84f..66677035b3 100644 --- a/apps/mobile/src/lib/kilo-pass/store-products.ts +++ b/apps/mobile/src/lib/kilo-pass/store-products.ts @@ -1,6 +1,6 @@ -import { type inferRouterOutputs, type RootRouter } from '@kilocode/trpc'; +import { type inferRouterOutputs, type MobileRouter } from '@kilocode/trpc/mobile'; -type RouterOutputs = inferRouterOutputs; +type RouterOutputs = inferRouterOutputs; type BackendStoreKiloPassProductOutput = RouterOutputs['kiloPass']['getMobileStoreProducts']['products'][number]; diff --git a/apps/mobile/src/lib/pr-review/discussion/review-discussion-types.ts b/apps/mobile/src/lib/pr-review/discussion/review-discussion-types.ts index b11cabba48..d845119d87 100644 --- a/apps/mobile/src/lib/pr-review/discussion/review-discussion-types.ts +++ b/apps/mobile/src/lib/pr-review/discussion/review-discussion-types.ts @@ -20,7 +20,7 @@ // unit-tested in plain Node and reused by the diff viewer if it later // wants to compute its own indicators from the same cache. -import { type inferRouterOutputs, type RootRouter } from '@kilocode/trpc'; +import { type inferRouterOutputs, type MobileRouter } from '@kilocode/trpc/mobile'; // GitHub's 8 review-comment reaction content values. The trpc DTO // exposes this as a plain `string`; we narrow to the union here so @@ -50,7 +50,7 @@ export const REVIEW_REACTION_CONTENTS: readonly ReviewReactionContent[] = [ // backend contract change (fields, nullability) is type-checked here rather // than silently drifting from a hand-copied shape (apps/mobile/AGENTS.md). // `reactions[].content` is typed as a plain `string` by tRPC; we narrow it to -type RouterOutputs = inferRouterOutputs; +type RouterOutputs = inferRouterOutputs; export type ReviewThreadsPage = RouterOutputs['githubPrReview']['listReviewThreads']; export type ReviewThread = ReviewThreadsPage['threads'][number]; export type ReviewComment = ReviewThread['comments'][number]; diff --git a/apps/mobile/src/lib/security-agent.ts b/apps/mobile/src/lib/security-agent.ts index 97473f3c45..063b13910a 100644 --- a/apps/mobile/src/lib/security-agent.ts +++ b/apps/mobile/src/lib/security-agent.ts @@ -1,8 +1,12 @@ -import { type inferRouterInputs, type inferRouterOutputs, type RootRouter } from '@kilocode/trpc'; +import { + type inferRouterInputs, + type inferRouterOutputs, + type MobileRouter, +} from '@kilocode/trpc/mobile'; import { type Href } from 'expo-router'; -type RouterInputs = inferRouterInputs; -type RouterOutputs = inferRouterOutputs; +type RouterInputs = inferRouterInputs; +type RouterOutputs = inferRouterOutputs; export type SecurityAgentConfig = RouterOutputs['securityAgent']['getConfig']; export type SecurityAgentConfigPatch = RouterInputs['securityAgent']['saveConfig']; diff --git a/apps/mobile/src/lib/session-list-cache.ts b/apps/mobile/src/lib/session-list-cache.ts index 10bce3e94f..4d570a4801 100644 --- a/apps/mobile/src/lib/session-list-cache.ts +++ b/apps/mobile/src/lib/session-list-cache.ts @@ -1,6 +1,6 @@ -import { type inferRouterOutputs, type RootRouter } from '@kilocode/trpc'; +import { type inferRouterOutputs, type MobileRouter } from '@kilocode/trpc/mobile'; -type RouterOutputs = inferRouterOutputs; +type RouterOutputs = inferRouterOutputs; export type SessionsListPage = RouterOutputs['cliSessionsV2']['list']; export type SessionsListData = { pages: SessionsListPage[]; pageParams: unknown[] }; diff --git a/apps/mobile/src/lib/trpc.ts b/apps/mobile/src/lib/trpc.ts index b59767c29a..1dea17de95 100644 --- a/apps/mobile/src/lib/trpc.ts +++ b/apps/mobile/src/lib/trpc.ts @@ -1,4 +1,4 @@ -import { type RootRouter } from '@kilocode/trpc'; +import { type MobileRouter } from '@kilocode/trpc/mobile'; import { createTRPCClient, httpBatchLink, httpLink, splitLink } from '@trpc/client'; import { createTRPCContext } from '@trpc/tanstack-react-query'; import * as SecureStore from 'expo-secure-store'; @@ -6,7 +6,7 @@ import * as SecureStore from 'expo-secure-store'; import { API_BASE_URL } from '@/lib/config'; import { AUTH_TOKEN_KEY } from '@/lib/storage-keys'; -export const { TRPCProvider, useTRPC } = createTRPCContext(); +export const { TRPCProvider, useTRPC } = createTRPCContext(); const trpcUrl = `${API_BASE_URL}/api/trpc`; @@ -18,7 +18,7 @@ async function getAuthHeaders() { return { Authorization: `Bearer ${token}` }; } -export const trpcClient = createTRPCClient({ +export const trpcClient = createTRPCClient({ links: [ splitLink({ condition: op => op.context.skipBatch === true, diff --git a/packages/trpc/package.json b/packages/trpc/package.json index a175cb38bd..f932d8e91f 100644 --- a/packages/trpc/package.json +++ b/packages/trpc/package.json @@ -10,6 +10,9 @@ "exports": { ".": { "types": "./dist/index.d.ts" + }, + "./mobile": { + "types": "./dist/mobile.d.ts" } }, "dependencies": { diff --git a/packages/trpc/rollup.config.mjs b/packages/trpc/rollup.config.mjs index b04da3ea67..53660b5fb5 100644 --- a/packages/trpc/rollup.config.mjs +++ b/packages/trpc/rollup.config.mjs @@ -15,43 +15,57 @@ function resolveDts(base) { return asFile; // fall through — let rollup report } -export default { - // These packages are declaration-boundary imports in the generated router - // types. Leave them external instead of asking rollup-plugin-dts to inline - // implementation package declarations into @kilocode/trpc's single d.ts. - external: [ - 'pg', - '@tanstack/react-query', - '@trpc/client', - 'next/server', - '@kilocode/encryption', - '@kilocode/kiloclaw-instance-tiers', - '@kilocode/worker-utils', - '@kilocode/worker-utils/security-remediation-policy', - '@kilocode/kilo-chat', - ], - input: './dist/tsc/packages/trpc/src/index.d.ts', - output: { - file: './dist/index.d.ts', - format: 'es', - banner: '// Auto-generated — do not edit. Rebuild with: pnpm --filter @kilocode/trpc run build', - }, - plugins: [ - { - name: 'resolve-aliases', - resolveId(source) { - // Resolve @/* path aliases to the tsc output (apps/web/src after monorepo restructure) - if (source.startsWith('@/')) { - return resolveDts(path.resolve(tscOut, 'apps/web/src', source.slice(2))); - } - // Resolve @kilocode/db sub-path imports - if (source === '@kilocode/db' || source.startsWith('@kilocode/db/')) { - const subpath = source === '@kilocode/db' ? 'index' : source.replace('@kilocode/db/', ''); - return resolveDts(path.resolve(tscOut, 'packages/db/src', subpath)); - } - return null; - }, +// These packages are declaration-boundary imports in the generated router +// types. Leave them external instead of asking rollup-plugin-dts to inline +// implementation package declarations into @kilocode/trpc's single d.ts. +const external = [ + 'pg', + '@tanstack/react-query', + '@trpc/client', + 'next/server', + '@kilocode/encryption', + '@kilocode/kiloclaw-instance-tiers', + '@kilocode/worker-utils', + '@kilocode/worker-utils/security-remediation-policy', + '@kilocode/kilo-chat', +]; + +const plugins = [ + { + name: 'resolve-aliases', + resolveId(source) { + // Resolve @/* path aliases to the tsc output (apps/web/src after monorepo restructure) + if (source.startsWith('@/')) { + return resolveDts(path.resolve(tscOut, 'apps/web/src', source.slice(2))); + } + // Resolve @kilocode/db sub-path imports + if (source === '@kilocode/db' || source.startsWith('@kilocode/db/')) { + const subpath = source === '@kilocode/db' ? 'index' : source.replace('@kilocode/db/', ''); + return resolveDts(path.resolve(tscOut, 'packages/db/src', subpath)); + } + return null; }, - dts(), - ], -}; + }, + dts(), +]; + +const banner = + '// Auto-generated — do not edit. Rebuild with: pnpm --filter @kilocode/trpc run build'; + +// Two separate single-artifact bundles. `mobile` composes only the namespaces +// the mobile app uses, so `dist/mobile.d.ts` is a smaller client-facing type +// surface than the full `dist/index.d.ts`. A shared bundle cannot shrink because +// MobileRouter ⊂ RootRouter, so each entry gets its own input → output. +function makeConfig(inputRel, outputFile) { + return { + external, + input: `./dist/tsc/packages/trpc/src/${inputRel}`, + output: { file: outputFile, format: 'es', banner }, + plugins, + }; +} + +export default [ + makeConfig('index.d.ts', './dist/index.d.ts'), + makeConfig('mobile.d.ts', './dist/mobile.d.ts'), +]; diff --git a/packages/trpc/src/mobile.ts b/packages/trpc/src/mobile.ts new file mode 100644 index 0000000000..46bf8bafd0 --- /dev/null +++ b/packages/trpc/src/mobile.ts @@ -0,0 +1,44 @@ +import 'server-only'; +import { createTRPCRouter } from '@/lib/trpc/init'; +import { organizationsRouter } from '@/routers/organizations/organization-router'; +import { userRouter } from '@/routers/user-router'; +import { cliSessionsV2Router } from '@/routers/cli-sessions-v2-router'; +import { cloudAgentNextRouter } from '@/routers/cloud-agent-next-router'; +import { githubAppsRouter } from '@/routers/github-apps-router'; +import { codeReviewRouter } from '@/routers/code-reviews/code-reviews-router'; +import { personalReviewAgentRouter } from '@/routers/code-reviews-router'; +import { securityAgentRouter } from '@/routers/security-agent-router'; +import { kiloPassRouter } from '@/routers/kilo-pass-router'; +import { kiloclawRouter } from '@/routers/kiloclaw-router'; +import { modelsRouter } from '@/routers/models-router'; +import { activeSessionsRouter } from '@/routers/active-sessions-router'; +import { modelPreferencesRouter } from '@/routers/model-preferences-router'; +import { githubPrReviewRouter } from '@/routers/github-pr-review-router'; +import { kiloChatRouter } from '@/routers/kilo-chat-router'; + +/** + * Mobile-scoped tRPC router. Composes only the namespaces the mobile app + * consumes, so `@kilocode/trpc/mobile` ships a smaller client-facing type + * surface than the full `RootRouter`. This is additive: `root-router.ts` is + * unchanged and remains the single source of the server router composition. + */ +const mobileRouter = createTRPCRouter({ + organizations: organizationsRouter, + user: userRouter, + cliSessionsV2: cliSessionsV2Router, + cloudAgentNext: cloudAgentNextRouter, + githubApps: githubAppsRouter, + codeReviews: codeReviewRouter, + personalReviewAgent: personalReviewAgentRouter, + securityAgent: securityAgentRouter, + kiloPass: kiloPassRouter, + kiloclaw: kiloclawRouter, + models: modelsRouter, + activeSessions: activeSessionsRouter, + modelPreferences: modelPreferencesRouter, + githubPrReview: githubPrReviewRouter, + kiloChat: kiloChatRouter, +}); + +export type MobileRouter = typeof mobileRouter; +export type { inferRouterInputs, inferRouterOutputs } from '@trpc/server'; From 1ab5a3a8a6013a0e20838db00e00da6219ff4362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 23 Jul 2026 07:08:39 +0200 Subject: [PATCH 06/14] ci: make mobile change selection dependency-aware --- .github/workflows/kilo-app-ci.yml | 12 +++++ .github/workflows/kilo-app-release.yml | 4 +- scripts/changed-workspaces.sh | 61 ++++++++++++++++++++++---- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/.github/workflows/kilo-app-ci.yml b/.github/workflows/kilo-app-ci.yml index f5cf650bcb..38263859a6 100644 --- a/.github/workflows/kilo-app-ci.yml +++ b/.github/workflows/kilo-app-ci.yml @@ -7,6 +7,12 @@ on: - 'apps/mobile/**' - 'packages/trpc/**' - 'apps/web/src/routers/**' + - 'packages/app-shared/**' + - 'packages/kilo-chat/**' + - 'packages/kilo-chat-hooks/**' + - 'packages/event-service/**' + - 'packages/notifications/**' + - 'packages/cloud-agent-sdk/**' - 'pnpm-lock.yaml' - '.github/workflows/kilo-app-ci.yml' pull_request: @@ -15,6 +21,12 @@ on: - 'apps/mobile/**' - 'packages/trpc/**' - 'apps/web/src/routers/**' + - 'packages/app-shared/**' + - 'packages/kilo-chat/**' + - 'packages/kilo-chat-hooks/**' + - 'packages/event-service/**' + - 'packages/notifications/**' + - 'packages/cloud-agent-sdk/**' - 'pnpm-lock.yaml' - '.github/workflows/kilo-app-ci.yml' workflow_call: diff --git a/.github/workflows/kilo-app-release.yml b/.github/workflows/kilo-app-release.yml index c5bd8196d9..c7a6851b40 100644 --- a/.github/workflows/kilo-app-release.yml +++ b/.github/workflows/kilo-app-release.yml @@ -37,8 +37,8 @@ jobs: echo "Last release tag: $LAST_TAG" - # Check for changes in kilo-app or trpc since that tag - CHANGES=$(git diff --name-only "$LAST_TAG"..HEAD -- apps/mobile/ packages/trpc/ pnpm-lock.yaml) + # Check for changes in kilo-app or its direct workspace:* inputs since that tag + CHANGES=$(git diff --name-only "$LAST_TAG"..HEAD -- apps/mobile/ packages/trpc/ packages/app-shared/ packages/kilo-chat/ packages/kilo-chat-hooks/ packages/event-service/ packages/notifications/ packages/cloud-agent-sdk/ pnpm-lock.yaml) if [ -z "$CHANGES" ]; then echo "No changes since $LAST_TAG — skipping build" diff --git a/scripts/changed-workspaces.sh b/scripts/changed-workspaces.sh index 40905719bc..642cc3341d 100755 --- a/scripts/changed-workspaces.sh +++ b/scripts/changed-workspaces.sh @@ -22,12 +22,53 @@ done base=$(git merge-base origin/main HEAD 2>/dev/null || true) -# If shared inputs changed, treat all workspaces as changed — shared package or -# lockfile updates can break any downstream workspace. -shared_changed=false +# Decide selection mode: +# force_all=true — include every workspace (lockfile/workspace-yaml changes +# genuinely can break any downstream workspace, and loose `packages/*` files +# outside any package dir don't have a single owner to derive dependents of). +# force_all=false and dependent_dirs_file populated — a `packages//**` +# path changed; only include that package plus its transitive workspace:* +# dependents. +# force_all=false and dependent_dirs_file empty — fall back to per-workspace +# direct file-diff check. +force_all=false +dependent_dirs_file=$(mktemp) +trap 'rm -f "$dependent_dirs_file"' EXIT if [ -n "$base" ]; then - if git diff --name-only "$base" -- pnpm-lock.yaml pnpm-workspace.yaml 'packages/**' | grep -q .; then - shared_changed=true + if git diff --name-only "$base" -- pnpm-lock.yaml pnpm-workspace.yaml | grep -q .; then + force_all=true + else + # List files changed under packages/**. Anything that doesn't match + # `packages//` (e.g. `packages/README.md`) is a loose file with no + # single owning package — fall back to force_all. + pkg_changes=$(git diff --name-only "$base" -- 'packages/**' || true) + if [ -n "$pkg_changes" ]; then + loose=$(printf '%s\n' "$pkg_changes" | grep -vE '^packages/[^/]+/' | head -1 || true) + if [ -n "$loose" ]; then + force_all=true + else + # Collect the top-level changed package dirs and union their transitive + # workspace:* dependents via pnpm's `...^` selector. + changed_pkg_dirs=$(printf '%s\n' "$pkg_changes" | awk -F/ '{print $1"/"$2}' | sort -u) + for pkg_dir in $changed_pkg_dirs; do + [ -f "$pkg_dir/package.json" ] || continue + pkg_name=$(node -e "console.log(require('./$pkg_dir/package.json').name)" 2>/dev/null) || continue + [ -n "$pkg_name" ] || continue + pnpm --filter "...^$pkg_name" ls --json --depth -1 2>/dev/null | node -e " + const pkgs = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); + for (const p of pkgs) { + if (!p.path) continue; + const rel = require('path').relative(process.cwd(), p.path); + if (rel && rel !== '.') console.log(rel); + } + " 2>/dev/null >> "$dependent_dirs_file" || true + # Always include the changed package itself; `...^` only matches + # true dependents, not the named package. + echo "$pkg_dir" >> "$dependent_dirs_file" + done + sort -u "$dependent_dirs_file" -o "$dependent_dirs_file" + fi + fi fi fi @@ -67,9 +108,13 @@ for dir in $workspace_dirs; do [ -n "$test_file_count" ] || continue # Check for file changes (if we have a merge base) - if [ -n "$base" ] && ! $shared_changed; then - changed_file=$(git diff --name-only "$base" -- "$dir/" | head -1 || true) - [ -n "$changed_file" ] || continue + if [ -n "$base" ] && ! $force_all; then + if [ -s "$dependent_dirs_file" ] && grep -qxF "$dir" "$dependent_dirs_file"; then + : # include: workspace is a transitive dependent of a changed package + else + changed_file=$(git diff --name-only "$base" -- "$dir/" | head -1 || true) + [ -n "$changed_file" ] || continue + fi fi name=$(node -e "console.log(require('./$dir/package.json').name)" 2>/dev/null) From eb4ab9f1bdf50dfd4b222cca927d35229987f6c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 23 Jul 2026 07:12:29 +0200 Subject: [PATCH 07/14] ci: select comparison SHA by GitHub event to avoid empty matrix on main --- scripts/changed-workspaces.sh | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/scripts/changed-workspaces.sh b/scripts/changed-workspaces.sh index 642cc3341d..26ae0bb427 100755 --- a/scripts/changed-workspaces.sh +++ b/scripts/changed-workspaces.sh @@ -20,7 +20,28 @@ while [[ $# -gt 0 ]]; do esac done -base=$(git merge-base origin/main HEAD 2>/dev/null || true) +# Compute the diff base SHA by GitHub event type. +# - `push` event: use the BEFORE sha from $GITHUB_EVENT_PATH so the diff covers +# the pushed range. `git merge-base origin/main HEAD` on a push to main +# resolves to HEAD, which would produce an empty matrix and skip the +# workspace-tests job. +# - `pull_request` event (and locally with no GitHub env): use the merge-base +# against origin/main to preserve prior PR behavior. +# - All-zeros before-sha (force-push / first push / branch creation) or any +# invalid/unreachable before-sha falls back to HEAD~1. +base="" +if [ "${GITHUB_EVENT_NAME:-}" = "push" ] && [ -n "${GITHUB_EVENT_PATH:-}" ] && [ -f "$GITHUB_EVENT_PATH" ]; then + before=$(jq -r '.before // ""' "$GITHUB_EVENT_PATH" 2>/dev/null || true) + if [ -n "$before" ] \ + && [ "$before" != "0000000000000000000000000000000000000000" ] \ + && git cat-file -e "$before" 2>/dev/null; then + base="$before" + else + base=$(git rev-parse --verify HEAD~1 2>/dev/null || true) + fi +else + base=$(git merge-base origin/main HEAD 2>/dev/null || true) +fi # Decide selection mode: # force_all=true — include every workspace (lockfile/workspace-yaml changes From 60db7f1c9e83c384c6f50cdb320f376065e6fe3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 23 Jul 2026 07:46:23 +0200 Subject: [PATCH 08/14] test(mobile): add mounted provider/TanStack test harness --- apps/mobile/package.json | 2 + .../render-with-providers.mounted.test.tsx | 59 ++++++++++ .../mobile/src/test/render-with-providers.tsx | 106 ++++++++++++++++++ apps/mobile/vitest.config.ts | 24 +--- apps/mobile/vitest.mounted.config.ts | 20 ++++ apps/mobile/vitest.pure.config.ts | 32 ++++++ pnpm-lock.yaml | 49 ++++++-- 7 files changed, 259 insertions(+), 33 deletions(-) create mode 100644 apps/mobile/src/test/render-with-providers.mounted.test.tsx create mode 100644 apps/mobile/src/test/render-with-providers.tsx create mode 100644 apps/mobile/vitest.mounted.config.ts create mode 100644 apps/mobile/vitest.pure.config.ts diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 78ba023261..fb1ab7bb17 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -99,9 +99,11 @@ "devDependencies": { "@sentry/cli": "catalog:", "@types/react": "19.2.14", + "@types/react-test-renderer": "^19.1.0", "@typescript/native-preview": "catalog:", "eslint-plugin-sonarjs": "4.0.3", "knip": "6.0.6", + "react-test-renderer": "^19.2.7", "typescript": "catalog:", "vitest": "catalog:" }, diff --git a/apps/mobile/src/test/render-with-providers.mounted.test.tsx b/apps/mobile/src/test/render-with-providers.mounted.test.tsx new file mode 100644 index 0000000000..713efee224 --- /dev/null +++ b/apps/mobile/src/test/render-with-providers.mounted.test.tsx @@ -0,0 +1,59 @@ +import { useQuery } from '@tanstack/react-query'; +import { expect, test, vi } from 'vitest'; + +import { createTestQueryClient, renderWithProviders, waitFor } from './render-with-providers'; + +// Representative mounted test: mounts a real React tree through the provider +// harness and asserts a TanStack Query transitions from pending to success, +// proving the harness renders, runs effects, and observes async state. +type Observed = { + status: string; + data: string | undefined; +}; + +function QueryConsumer({ + queryFn, + onRender, +}: { + queryFn: () => Promise; + onRender: (o: Observed) => void; +}) { + const { data, status } = useQuery({ queryKey: ['harness-probe'], queryFn }); + onRender({ status, data }); + return null; +} + +test('renderWithProviders mounts a QueryClient tree and resolves a query', async () => { + const queryFn = vi.fn().mockResolvedValue('loaded'); + const queryClient = createTestQueryClient(); + const renders: Observed[] = []; + const latest = (): Observed => { + const last = renders.at(-1); + if (!last) { + throw new Error('component has not rendered yet'); + } + return last; + }; + + const { unmount } = await renderWithProviders( + { + renders.push(o); + }} + />, + { queryClient } + ); + + // Initial mount: query is pending, no data yet. + expect(latest().status).toBe('pending'); + expect(latest().data).toBeUndefined(); + + // Let the query settle, then assert the resolved state was rendered. + await waitFor(() => latest().status === 'success'); + + expect(latest().data).toBe('loaded'); + expect(queryFn).toHaveBeenCalledTimes(1); + + unmount(); +}); diff --git a/apps/mobile/src/test/render-with-providers.tsx b/apps/mobile/src/test/render-with-providers.tsx new file mode 100644 index 0000000000..ba8eae6b28 --- /dev/null +++ b/apps/mobile/src/test/render-with-providers.tsx @@ -0,0 +1,106 @@ +/* eslint-disable typescript-eslint/no-deprecated -- react-test-renderer is the DOM-free renderer used to mount React/RN trees under vitest (node env, no jsdom); its React 19 deprecation notice points to the DOM-based Testing Library, which cannot render this app's non-DOM tree, and @testing-library/react-native (which itself wraps react-test-renderer) cannot be transformed by the current vitest pipeline (react-native ships Flow). */ +import { type ComponentType, createElement, type ReactElement, type ReactNode } from 'react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import TestRenderer, { act } from 'react-test-renderer'; + +/** + * Mounted-test harness for the mobile app's data layer. + * + * The app's runtime provider stack (see `src/components/app-root-providers.tsx`) + * is GestureHandlerRootView -> TRPCProvider -> QueryClientProvider -> + * QueryClientNativeLifecycle -> AuthProvider -> OrganizationProvider -> + * ActionSheetProvider -> PortalHost. TanStack Query (`QueryClientProvider`) is + * the minimal provider needed to exercise data-fetching behavior, so it is the + * default here. + * + * Rendering uses `react-test-renderer`, which is DOM-free (no jsdom): the mounted + * tests run in a `node` environment. This harness deliberately does NOT statically + * import the app's TRPC/Auth/Organization providers: those pull in `react-native`, + * whose Flow-typed source the vitest (rolldown) transform cannot currently parse. + * Callers that need extra providers pass a `wrapper` component; when an RN-capable + * transform is available, real app providers can be dropped in through it. + */ + +type RenderWithProvidersOptions = { + /** Reuse a specific client (e.g. to seed cache); defaults to a fresh test client. */ + queryClient?: QueryClient; + /** Optional extra provider composition wrapped inside `QueryClientProvider`. */ + wrapper?: ComponentType<{ children: ReactNode }>; +}; + +type RenderWithProvidersResult = { + renderer: TestRenderer.ReactTestRenderer; + queryClient: QueryClient; + /** Unmount the tree and clear the query cache. */ + unmount: () => void; +}; + +/** + * Build a `QueryClient` configured for deterministic tests: retries disabled so a + * rejected query surfaces immediately, and no background refetching. + */ +export function createTestQueryClient(): QueryClient { + return new QueryClient({ + defaultOptions: { + queries: { retry: false, gcTime: Infinity, staleTime: Infinity }, + mutations: { retry: false }, + }, + }); +} + +/** + * Mount `ui` inside `QueryClientProvider` (plus an optional caller `wrapper`) using + * `react-test-renderer`, flushing effects within `act`. Returns the renderer and the + * `QueryClient` so tests can assert async state. + */ +export async function renderWithProviders( + ui: ReactElement, + options: RenderWithProvidersOptions = {} +): Promise { + const queryClient = options.queryClient ?? createTestQueryClient(); + const Wrapper = options.wrapper; + + const inner = Wrapper ? createElement(Wrapper, null, ui) : ui; + const tree = createElement(QueryClientProvider, { client: queryClient }, inner); + + const ref: { current: TestRenderer.ReactTestRenderer | undefined } = { current: undefined }; + await act(async () => { + ref.current = TestRenderer.create(tree); + await Promise.resolve(); + }); + const created = ref.current; + if (!created) { + throw new Error('renderWithProviders: renderer was not created'); + } + + return { + renderer: created, + queryClient, + unmount: () => { + act(() => { + created.unmount(); + }); + queryClient.clear(); + }, + }; +} + +/** + * Repeatedly flush effects until `predicate` returns true or the attempt budget is + * exhausted. Each attempt is wrapped in `act`, so state updates from settling + * queries are applied before the next check. Throws if the predicate never holds. + */ +export async function waitFor(predicate: () => boolean, attempts = 50): Promise { + for (let i = 0; i < attempts; i += 1) { + if (predicate()) { + return; + } + // eslint-disable-next-line no-await-in-loop -- polling must flush and re-check sequentially between act cycles + await act(async () => { + await Promise.resolve(); + }); + } + if (!predicate()) { + throw new Error(`waitFor: condition not met after ${attempts} attempts`); + } +} diff --git a/apps/mobile/vitest.config.ts b/apps/mobile/vitest.config.ts index eaaffc962c..12127e60d2 100644 --- a/apps/mobile/vitest.config.ts +++ b/apps/mobile/vitest.config.ts @@ -1,29 +1,7 @@ -import { fileURLToPath } from 'node:url'; - import { defineConfig } from 'vitest/config'; export default defineConfig({ - resolve: { - alias: { - '@': fileURLToPath(new URL('./src', import.meta.url)), - }, - }, test: { - name: 'mobile-onboarding', - environment: 'node', - include: [ - 'src/lib/*.test.ts', - 'src/lib/agent-attachments/**/*.test.ts', - 'src/lib/auth/**/*.test.ts', - 'src/lib/apple-iap/**/*.test.ts', - 'src/lib/apple-iap/**/*.test.tsx', - 'src/lib/hooks/**/*.test.ts', - 'src/lib/kilo-pass/**/*.test.ts', - 'src/lib/kilo-pass/**/*.test.tsx', - 'src/lib/onboarding/**/*.test.ts', - 'src/lib/pr-review/**/*.test.ts', - 'src/lib/voice-input/**/*.test.ts', - 'src/components/**/*.test.ts', - ], + projects: ['./vitest.pure.config.ts', './vitest.mounted.config.ts'], }, }); diff --git a/apps/mobile/vitest.mounted.config.ts b/apps/mobile/vitest.mounted.config.ts new file mode 100644 index 0000000000..b06399cb0e --- /dev/null +++ b/apps/mobile/vitest.mounted.config.ts @@ -0,0 +1,20 @@ +import { fileURLToPath } from 'node:url'; + +import { defineProject } from 'vitest/config'; + +// Mounted tests: render a real React tree (providers + TanStack Query) with +// react-test-renderer, which is DOM-free, so a `node` environment is used (no +// jsdom). Files match `*.mounted.test.tsx` so they never run in the pure +// project. +export default defineProject({ + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + test: { + name: 'mobile-mounted', + environment: 'node', + include: ['src/**/*.mounted.test.tsx'], + }, +}); diff --git a/apps/mobile/vitest.pure.config.ts b/apps/mobile/vitest.pure.config.ts new file mode 100644 index 0000000000..d5fe7f6f32 --- /dev/null +++ b/apps/mobile/vitest.pure.config.ts @@ -0,0 +1,32 @@ +import { fileURLToPath } from 'node:url'; + +import { defineProject } from 'vitest/config'; + +// Pure-logic tests: node environment, no React mounting. This is the original +// mobile vitest project, kept unchanged so the existing ~205 suites are +// unaffected by the mounted-test harness. +export default defineProject({ + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + test: { + name: 'mobile-pure', + environment: 'node', + include: [ + 'src/lib/*.test.ts', + 'src/lib/agent-attachments/**/*.test.ts', + 'src/lib/auth/**/*.test.ts', + 'src/lib/apple-iap/**/*.test.ts', + 'src/lib/apple-iap/**/*.test.tsx', + 'src/lib/hooks/**/*.test.ts', + 'src/lib/kilo-pass/**/*.test.ts', + 'src/lib/kilo-pass/**/*.test.tsx', + 'src/lib/onboarding/**/*.test.ts', + 'src/lib/pr-review/**/*.test.ts', + 'src/lib/voice-input/**/*.test.ts', + 'src/components/**/*.test.ts', + ], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 417b31f72d..d6f68adada 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -495,6 +495,9 @@ importers: '@types/react': specifier: 19.2.14 version: 19.2.14 + '@types/react-test-renderer': + specifier: ^19.1.0 + version: 19.1.0 '@typescript/native-preview': specifier: 'catalog:' version: 7.0.0-dev.20260514.1 @@ -504,6 +507,9 @@ importers: knip: specifier: 6.0.6 version: 6.0.6 + react-test-renderer: + specifier: ^19.2.7 + version: 19.2.7(react@19.2.0) typescript: specifier: 'catalog:' version: 5.9.3 @@ -1002,7 +1008,7 @@ importers: version: 1.1.5(react-dom@19.2.6(react@19.2.6))(react@19.2.6) recharts: specifier: 3.8.1 - version: 3.8.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react-is@19.2.4)(react@19.2.6)(redux@5.0.1) + version: 3.8.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react-is@19.2.7)(react@19.2.6)(redux@5.0.1) redis: specifier: 5.11.0 version: 5.11.0 @@ -1223,7 +1229,7 @@ importers: version: 7.0.0-dev.20260514.1 jest: specifier: 30.3.0 - version: 30.3.0(@types/node@24.12.4)(node-notifier@10.0.1) + version: 30.3.0(@types/node@25.5.2)(node-notifier@10.0.1) typescript: specifier: 'catalog:' version: 5.9.3 @@ -9497,6 +9503,9 @@ packages: peerDependencies: '@types/react': ^19.2.0 + '@types/react-test-renderer@19.1.0': + resolution: {integrity: sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ==} + '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} @@ -15823,6 +15832,9 @@ packages: react-is@19.2.4: resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==} + react-is@19.2.7: + resolution: {integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==} + react-markdown@10.1.0: resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} peerDependencies: @@ -15967,6 +15979,11 @@ packages: '@types/react': optional: true + react-test-renderer@19.2.7: + resolution: {integrity: sha512-U4TyPDJ9MsC8rFimXuJum8w40aPc9kbOZYO8Pc2/4A884i8hwJsMNA/JNyuOc/f2/37wHvk7HjpVl1V4re7Dig==} + peerDependencies: + react: ^19.2.7 + react-turnstile@1.1.5: resolution: {integrity: sha512-VTL5OeHAatzCEVQxAZox70/TPmhKxEbNgtr++dg+8zm9QrWKuoU9E0+7gqmycOSCDZuJFzvMMLKQb5PVUPLV6w==} peerDependencies: @@ -19805,7 +19822,7 @@ snapshots: cjs-module-lexer: 1.2.3 esbuild: 0.27.4 miniflare: 4.20260603.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@24.12.4)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) wrangler: 4.98.0(@cloudflare/workers-types@4.20260605.1)(bufferutil@4.1.0)(utf-8-validate@6.0.6) zod: 3.25.76 transitivePeerDependencies: @@ -24418,7 +24435,7 @@ snapshots: - supports-color - webpack - '@sentry/node-core@10.43.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)': + '@sentry/node-core@10.43.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0)': dependencies: '@sentry/core': 10.43.0 '@sentry/opentelemetry': 10.43.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) @@ -24427,7 +24444,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 2.6.0(@opentelemetry/api@1.9.1) '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.6.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.40.0 @@ -24466,7 +24483,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.40.0 '@prisma/instrumentation': 7.2.0(@opentelemetry/api@1.9.0) '@sentry/core': 10.43.0 - '@sentry/node-core': 10.43.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) + '@sentry/node-core': 10.43.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) '@sentry/opentelemetry': 10.43.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.40.0) import-in-the-middle: 2.0.6 transitivePeerDependencies: @@ -26011,6 +26028,10 @@ snapshots: dependencies: '@types/react': 19.2.14 + '@types/react-test-renderer@19.1.0': + dependencies: + '@types/react': 19.2.14 + '@types/react@19.2.14': dependencies: csstype: 3.2.3 @@ -26270,7 +26291,7 @@ snapshots: obug: 2.1.1 std-env: 4.0.0 tinyrainbow: 3.1.0 - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@24.12.4)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) '@vitest/expect@3.2.4': dependencies: @@ -26348,7 +26369,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@24.12.4)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) '@vitest/utils@3.2.4': dependencies: @@ -33741,6 +33762,8 @@ snapshots: react-is@19.2.4: {} + react-is@19.2.7: {} + react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.6): dependencies: '@types/hast': 3.0.4 @@ -33976,6 +33999,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 + react-test-renderer@19.2.7(react@19.2.0): + dependencies: + react: 19.2.0 + react-is: 19.2.7 + scheduler: 0.27.0 + react-turnstile@1.1.5(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: react: 19.2.6 @@ -34033,7 +34062,7 @@ snapshots: tiny-invariant: 1.3.3 tslib: 2.8.1 - recharts@3.8.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react-is@19.2.4)(react@19.2.6)(redux@5.0.1): + recharts@3.8.1(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react-is@19.2.7)(react@19.2.6)(redux@5.0.1): dependencies: '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.6)(redux@5.0.1))(react@19.2.6) clsx: 2.1.1 @@ -34043,7 +34072,7 @@ snapshots: immer: 10.2.0 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - react-is: 19.2.4 + react-is: 19.2.7 react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.6)(redux@5.0.1) reselect: 5.1.1 tiny-invariant: 1.3.3 From 80cc5dc1b9267860c4bb0e5508682320f2e172bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 23 Jul 2026 07:50:38 +0200 Subject: [PATCH 09/14] perf(mobile): import used JetBrains weights only and drop the unused Material-Symbols font chain --- apps/mobile/metro.config.js | 25 +++++++++++++++++++++++++ apps/mobile/src/app/_layout.tsx | 10 ++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/apps/mobile/metro.config.js b/apps/mobile/metro.config.js index 8d359e73c8..126489ffae 100644 --- a/apps/mobile/metro.config.js +++ b/apps/mobile/metro.config.js @@ -17,6 +17,31 @@ config.resolver.nodeModulesPaths = [ path.resolve(monorepoRoot, 'node_modules'), ]; +// Drop the unused Material Symbols font chain from the bundle. +// +// `expo-router`'s statically pulls `expo-symbols` (via withLayoutContext -> +// native-tabs -> materialIconConverter.android). `expo-symbols` require()s all 7 +// Android Material Symbols weights (~6.7MB) at module scope, through both the +// `@expo-google-fonts/material-symbols` barrel (SymbolView) and the per-weight +// subpaths (android/weights/*). This app renders lucide icons and never renders +// /, so those fonts are loaded-but-unused: the `.ttf` +// values are only consumed lazily inside SymbolView (via useFonts), which is +// never mounted. Resolving these specifiers to an empty module removes the font +// bytes while leaving the never-rendered code paths harmlessly referencing +// `undefined`. iOS uses native SF Symbols and never reaches this chain. +const MATERIAL_SYMBOLS_PKG = '@expo-google-fonts/material-symbols'; +const upstreamResolveRequest = config.resolver.resolveRequest; +config.resolver.resolveRequest = (context, moduleName, platform) => { + if ( + moduleName === MATERIAL_SYMBOLS_PKG || + moduleName.startsWith(`${MATERIAL_SYMBOLS_PKG}/`) + ) { + return { type: 'empty' }; + } + const resolve = upstreamResolveRequest || context.resolveRequest; + return resolve(context, moduleName, platform); +}; + module.exports = withNativewind(config, { inlineVariables: false, }); diff --git a/apps/mobile/src/app/_layout.tsx b/apps/mobile/src/app/_layout.tsx index ac5efa61da..8b9b7934bd 100644 --- a/apps/mobile/src/app/_layout.tsx +++ b/apps/mobile/src/app/_layout.tsx @@ -1,10 +1,12 @@ import '../global.css'; import '@/lib/cloud-agent-runtime'; -import { - JetBrainsMono_500Medium, - JetBrainsMono_600SemiBold, -} from '@expo-google-fonts/jetbrains-mono'; +// Deep imports of only the two weights this app renders. The package barrel +// (`@expo-google-fonts/jetbrains-mono`) require()s all 16 weights at module +// scope and Metro does not tree-shake, so importing it ships ~1.63MB of unused +// font bytes. The per-weight subpaths pull only the two used `.ttf` files. +import { JetBrainsMono_500Medium } from '@expo-google-fonts/jetbrains-mono/500Medium'; +import { JetBrainsMono_600SemiBold } from '@expo-google-fonts/jetbrains-mono/600SemiBold'; import { ThemeProvider } from '@react-navigation/native'; import * as Sentry from '@sentry/react-native'; import { isRunningInExpoGo } from 'expo'; From 7119bd1b534cf20cb5c0a8ecaf44e51686b4bb1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 23 Jul 2026 08:06:19 +0200 Subject: [PATCH 10/14] chore(mobile): apply repo formatting to metro.config.js --- apps/mobile/metro.config.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/mobile/metro.config.js b/apps/mobile/metro.config.js index 126489ffae..f0c0d94910 100644 --- a/apps/mobile/metro.config.js +++ b/apps/mobile/metro.config.js @@ -32,10 +32,7 @@ config.resolver.nodeModulesPaths = [ const MATERIAL_SYMBOLS_PKG = '@expo-google-fonts/material-symbols'; const upstreamResolveRequest = config.resolver.resolveRequest; config.resolver.resolveRequest = (context, moduleName, platform) => { - if ( - moduleName === MATERIAL_SYMBOLS_PKG || - moduleName.startsWith(`${MATERIAL_SYMBOLS_PKG}/`) - ) { + if (moduleName === MATERIAL_SYMBOLS_PKG || moduleName.startsWith(`${MATERIAL_SYMBOLS_PKG}/`)) { return { type: 'empty' }; } const resolve = upstreamResolveRequest || context.resolveRequest; From 5db889ad1eb52210a251e6aafe2fec76724e2d65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Thu, 23 Jul 2026 08:11:53 +0200 Subject: [PATCH 11/14] test(mobile): make waitFor yield to macrotasks so query settle is deterministic in CI --- apps/mobile/src/test/render-with-providers.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/mobile/src/test/render-with-providers.tsx b/apps/mobile/src/test/render-with-providers.tsx index ba8eae6b28..604a9782a8 100644 --- a/apps/mobile/src/test/render-with-providers.tsx +++ b/apps/mobile/src/test/render-with-providers.tsx @@ -95,9 +95,14 @@ export async function waitFor(predicate: () => boolean, attempts = 50): Promise< if (predicate()) { return; } + // Yield to the macrotask queue (not just microtasks): TanStack Query flushes + // observer notifications through its scheduler, so a bare `Promise.resolve()` + // can loop without ever observing the settled query. // eslint-disable-next-line no-await-in-loop -- polling must flush and re-check sequentially between act cycles await act(async () => { - await Promise.resolve(); + await new Promise(resolve => { + setTimeout(resolve, 0); + }); }); } if (!predicate()) { From 23deaf45e6d1a2871de162a856338c4aff987535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Mon, 27 Jul 2026 14:45:07 +0200 Subject: [PATCH 12/14] docs(mobile): record iOS 26.5 scheme-prompt wording blocker in workflow learnings --- apps/mobile/.kilo/WORKFLOW_LEARNINGS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md b/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md index 9f1ec49fd2..608b763784 100644 --- a/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md +++ b/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md @@ -5,3 +5,9 @@ Environment blockers and their fixes, recorded by the planner or orchestrator fo ## Planner ## Orchestrator + +### iOS 26.5 scheme-confirmation prompt wording breaks login.sh on a fresh install + +- Symptom: on a freshly installed dev client, `apps/mobile/e2e/login.sh` fails its settle assertion with the simulator stuck on the home screen under a SpringBoard dialog `Open in "Kilo"?` (Cancel/Open). +- Cause: iOS 26.5 reworded the custom-scheme confirmation from `Open this page in "Kilo"?` to `Open in "Kilo"?`. The matchers in `e2e/flows/open-app.yaml` and `e2e/flows/settle-app.yaml` only know the old text, so the bounded optional-prompt tap never fires. The dialog appears exactly once per install (first external open of the `exp+kilo-app` scheme); iOS does not re-prompt afterwards. +- Fix: tap `Open` once yourself (a two-line temp Maestro flow with `appId: host.springboard` and `tapOn: { text: 'Open', optional: true }`, or any equivalent), then re-run `login.sh` — it is idempotent and completes. Do not reinstall afterwards: a reinstall re-arms the one-time prompt. If the flows are ever updated, add `Open in "Kilo"\?` as an alternative in the same regexes rather than replacing the old text, so older iOS versions keep working. From 12228a200223b8d41a2a21296bd291c34d7e932b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Mon, 27 Jul 2026 14:57:33 +0200 Subject: [PATCH 13/14] docs(mobile): record role-agent payload-size kill in workflow learnings --- apps/mobile/.kilo/WORKFLOW_LEARNINGS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md b/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md index 608b763784..097eeed8b4 100644 --- a/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md +++ b/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md @@ -11,3 +11,9 @@ Environment blockers and their fixes, recorded by the planner or orchestrator fo - Symptom: on a freshly installed dev client, `apps/mobile/e2e/login.sh` fails its settle assertion with the simulator stuck on the home screen under a SpringBoard dialog `Open in "Kilo"?` (Cancel/Open). - Cause: iOS 26.5 reworded the custom-scheme confirmation from `Open this page in "Kilo"?` to `Open in "Kilo"?`. The matchers in `e2e/flows/open-app.yaml` and `e2e/flows/settle-app.yaml` only know the old text, so the bounded optional-prompt tap never fires. The dialog appears exactly once per install (first external open of the `exp+kilo-app` scheme); iOS does not re-prompt afterwards. - Fix: tap `Open` once yourself (a two-line temp Maestro flow with `appId: host.springboard` and `tapOn: { text: 'Open', optional: true }`, or any equivalent), then re-run `login.sh` — it is idempotent and completes. Do not reinstall afterwards: a reinstall re-arms the one-time prompt. If the flows are ever updated, add `Open in "Kilo"\?` as an alternative in the same regexes rather than replacing the old text, so older iOS versions keep working. + +### Role-agent kilo run dies silently when the session payload exceeds the pruning limit + +- Symptom: a dispatched `kilo run` role agent (observed: mobile-e2e-verifier) exits mid-task with no error and no final report; its transcript just stops, often right after a large tool output. +- Cause: the agent's session payload grows past the pruning limit (`opencode.log` shows `payload still large after pruning ... size=3042931` at the kill time) and the harness terminates the run. E2E agents inflate the payload fast: full `maestro hierarchy` dumps (~80 KB), full `pnpm dev:capture mobile` panes with the QR art, repeated echo of the same long command output, screenshots read into context. +- Fix: steer the redispatch with an explicit output-discipline constraint: every shell command ends in a hard cap (`| tail -c 1500` / `| tail -5`), hierarchies and captures go to files and only greps/counts are printed, screenshots are not re-read into context, docs are inlined in the handoff instead of re-read, and the final report has a line cap. A round-3 dispatch with these rules survives long enough to finish; rounds 1-2 without them died at ~4-10 minutes. From a8ac9e151feb7f8e1d2837aabcf833777eed4cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20=C5=A0=C4=87eki=C4=87?= Date: Mon, 27 Jul 2026 18:35:19 +0200 Subject: [PATCH 14/14] docs(mobile): record fake-llm deterministic cloud-turn recipe in workflow learnings --- apps/mobile/.kilo/WORKFLOW_LEARNINGS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md b/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md index 097eeed8b4..635c65df06 100644 --- a/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md +++ b/apps/mobile/.kilo/WORKFLOW_LEARNINGS.md @@ -17,3 +17,9 @@ Environment blockers and their fixes, recorded by the planner or orchestrator fo - Symptom: a dispatched `kilo run` role agent (observed: mobile-e2e-verifier) exits mid-task with no error and no final report; its transcript just stops, often right after a large tool output. - Cause: the agent's session payload grows past the pruning limit (`opencode.log` shows `payload still large after pruning ... size=3042931` at the kill time) and the harness terminates the run. E2E agents inflate the payload fast: full `maestro hierarchy` dumps (~80 KB), full `pnpm dev:capture mobile` panes with the QR art, repeated echo of the same long command output, screenshots read into context. - Fix: steer the redispatch with an explicit output-discipline constraint: every shell command ends in a hard cap (`| tail -c 1500` / `| tail -5`), hierarchies and captures go to files and only greps/counts are printed, screenshots are not re-read into context, docs are inlined in the handoff instead of re-read, and the final report has a line cap. A round-3 dispatch with these rules survives long enough to finish; rounds 1-2 without them died at ~4-10 minutes. + +### Deterministic cloud-agent turns via fake-llm (real-provider turns stall E2E) + +- Symptom: the E2E verifier starts an app-driven cloud-agent session and the turn never completes: "Considering next steps" for 9+ minutes, or `terminal-failed`, while real-provider routing burns credits and time. Round after round cannot score the turn-based acceptance criteria. +- Cause: `KILO_OPENROUTER_BASE` in `services/cloud-agent-next/.dev.vars` points at nextjs (`http://localhost:/api`), i.e. real model providers — non-deterministic and slow in dev. Three environment traps compound it: (1) `e2e/login.sh` signs out first, so a verifier that "re-runs login" mid-task strands the app on the login screen (never let it); (2) a hermetic `platform_integrations` row with a made-up repo (e.g. `kilo-stub/...`) passes the composer repo picker but fails dispatch with `GitHub token or active app installation required` — the cached repo must belong to a real, locally mintable installation (e.g. `iscekic/getel`, installation `144771093`); (3) fake-llm stock only knows model `fake-deterministic`, so worker-side `models/validate` and the sandbox CLI's catalog lookup both reject the app's `kilo-auto/*` selection (`model_missing`, classified `managed_model_configuration`). +- Fix: route the turn to fake-llm and re-apply this harness patch set (deliberately not committed — harness-local): set `KILO_OPENROUTER_BASE=http://localhost:<8811+portOffset>/api` in `services/cloud-agent-next/.dev.vars` and `pnpm dev:restart cloud-agent-next`; in `services/cloud-agent-next/test/e2e/fake-llm-server.ts` accept `kilo-auto/*` + `kilo/*` in `handleModelValidation` AND add catalog entries for `kilo-auto/frontier|balanced|efficient` (clone `FAKE_MODEL`, non-zero pricing e.g. `0.000003`/`0.000015` so cost computes non-zero); `pnpm dev:restart fake-llm`. Prompt `__fake__:echo:` in the composer — the assistant replies `` with usage, the session auto-titles ``, and the title-model call is handled by design. A cloud turn then completes in ~30s with cost pill + model label visible. Revert the patch, restore `.dev.vars`, and delete the fixture rows when done.