// `binaries {}` — экспериментальный API KGP, `compose.uiTest` — экспериментальная // библиотека Compose (компилятор требует явного opt-in в скрипте). @file:OptIn( org.jetbrains.compose.ExperimentalComposeLibrary::class, org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class, ) plugins { alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.kotlin.serialization) // UI — Compose Multiplatform. `kotlin-compose` — компиляторный плагин // (@Composable), `compose` — рантайм/DSL (`compose.material3`, ...). alias(libs.plugins.kotlin.compose) alias(libs.plugins.compose) // `application` plugin (он тянет `java`) **несовместим** с // `kotlin("multiplatform")` — проверка `checkKotlinGradlePluginConfigurationErrors` // падает на старте. В KMP executable настраивается через // `kotlin { jvm { binaries { executable { ... } } } }` ниже — он создаёт // `run` / `installDist` / jar с mainClass автоматически. } group = "pw.binom.agentik.desktop" version = "0.1.0-SNAPSHOT" kotlin { // KMP-проект с **одной** целью — JVM. Структура каталогов // `src/jvmMain/kotlin/...` (а не `src/main/kotlin/...`), чтобы при // расширении целями (iOS / Native) не пришлось переименовывать. // Toolchain 21 = как у agentik (`:client` / `:journal-ksqlite` тоже // jvmToolchain(21)) — иначе Gradle тащит второй JDK через foojay. jvmToolchain(21) jvm { binaries { executable { mainClass.set("pw.binom.agentik.desktop.MainKt") // `jvmJar` создаётся автоматически; `run` запускает main. } } } sourceSets { jvmMain.dependencies { // --- Agentik (композитная сборка, см. settings.gradle.kts) --- // `AgentikAgent` / `wrapWithLocalConversationCache` / // `ReconnectingOutbox` — всё что нужно для клиента уже здесь. implementation(libs.agentik.client) // Контракты `MessageRecord` / `ConversationRecord` / // `MutableJournalStore` / `MutableConversationStore`. implementation(libs.agentik.journal.api) // SQLite-реализации обоих store'ов из `:journal-ksqlite`. implementation(libs.agentik.journal.ksqlite) // `Event` / `AgentEvent` / `CommonEvent` / `OutboxStore` (live-поток). implementation(libs.agentik.outbox.api) // `Agent` / `Conversation` / `AgentInfo` / `Content` — proto-контракты. implementation(libs.agentik.proto) // --- Ktor client engine (передаётся в AgentikAgent как engineFactory) --- implementation(libs.ktor.client.cio) // --- Низкоуровневая БД (`SQLiteConnection` / prepared statements) --- implementation(libs.ksqlite) // --- Kotlinx --- implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.serialization.json) // --- UI: Compose Multiplatform (markdown-рендер + экраны) --- implementation(compose.foundation) implementation(compose.ui) implementation(compose.material3) implementation(compose.materialIconsExtended) // Оконный рантайм Compose Desktop (Window/application) + skiko // для текущей ОС. Без него `compose.ui.window.application` не резолвится. implementation(compose.desktop.currentOs) // --- Markdown: парсер (ru.otpbank.ai:markdown, обёртка над // org.intellij.markdown) + вендоренный рендер сегментов. --- implementation(libs.markdown) // --- Голос: микрофон → VAD → распознавание (см. MIC-ASR-SEARCH.md). // JVM-реализация микрофона — `javax.sound.sampled`. --- implementation(libs.mic.api) implementation(libs.asr.api) implementation(libs.asr.vosk) implementation(libs.vad) // --- Логирование --- implementation(libs.kotlin.logging) implementation(libs.logback.classic) } jvmTest.dependencies { implementation(libs.kotlin.test) implementation(libs.kotlinx.coroutines.test) implementation(libs.junit.jupiter) // Оффскрин-рендер Compose: прогоняем реальную тему/компоненты // в ImageComposeScene и проверяем палитру по пикселям. implementation(compose.uiTest) // E2E: поднимаем настоящий `:server` (HTTP/SSE) поверх // in-memory-агента и гоняем десктопный стек по живому протоколу. implementation(libs.agentik.server) implementation(libs.agentik.journal.inmemory) implementation(libs.agentik.outbox.inmemory) implementation(libs.ktor.server.cio) } } } // `application` плагин в KMP конфликтует с `kotlin("multiplatform")`, // executable объявлен выше в `kotlin { jvm { binaries { executable { ... } } } }` // — он автоматически создаёт `run` / `installDist` и jvmJar. tasks.withType().configureEach { useJUnitPlatform() // Один JVM на тест-класс. Причина не в изоляции данных, а в нативных // библиотеках: skiko (Compose) и C++-стек распознавания (Vosk/Kaldi, ONNX) // конфликтуют по порядку загрузки — тот, кто грузится ПОСЛЕ skiko, падает // с SIGSEGV. Классы, которым нужен нативный ASR, обязаны прогревать его // первым делом (VoiceNative / RealSpeechIT), а skiko-классы — не мешать им. // forkEvery = 1 гарантирует, что порядок классов не важен. forkEvery = 1 // Изолируем данные приложения от реального $HOME разработчика: // AppPaths читает AGENTIK_DESKTOP_HOME, тесты пишут в build/. environment( "AGENTIK_DESKTOP_HOME", layout.buildDirectory.dir("test-agentik-home").get().asFile.absolutePath, ) }