Mimari
Genel bakış
Bölüm başlığı “Genel bakış”Control Center, portlar ve adaptörler deseniyle özellik öncelikli Clean Architecture’ı izler. Kod tabanı iş alanları (özellikler) etrafında düzenlenmiştir ve katı bağımlılık kuralları otomatik bir mimari testiyle uygulanır.
Headless bir sunucu üzerinde ince istemci
Bölüm başlığı “Headless bir sunucu üzerinde ince istemci”Control Center bir ince istemci mimarisidir. Hiçbir istemci veritabanını açmaz — verinin sahibi headless bir cc_server sürecidir (küçük bir global.db artı her çalışma alanı için tembelce açılan bir SQLite dosyası) ve veriyi WebSocket RPC üzerinden sunar. Masaüstü uygulaması, web derlemesi ve telefon yoldaşı, hepsi o tek RPC bağlantısı üzerinden çizen istemcilerdir.
Masaüstü, ilk açılışta sunucusuna nasıl ulaşacağını seçer: yerel (bu makinede bir cc_server başlatır ve loopback üzerinden konuşur — varsayılan budur) ya da uzak (başka bir yerdeki cc_server’a güvenli bir WebSocket üzerinden bağlanır). Web istemcisi her zaman uzaktır. Önyükleme çözümleyicisi (bootstrap/server_backend.dart), Riverpod var olmadan önce yolu seçer, ardından rpcClientProvider’ı bağlı istemciyle geçersiz kılar — böylece her özellik yerel bir veritabanı yerine sunucu üzerinden okur ve yazar.
RPC katmanının kendisi durumsuzdur: bir oturumun kimliği doğrulanır, ama oturum bir çalışma alanına bağlı değildir. Çalışma alanı kapsamlı her çağrı, kendi workspace_id değerini argümanlarında taşır ve gönderici böyle bir çağrıyı bu değer olmadan reddeder. İki istemcinin tek bir sunucuda farklı etkin çalışma alanları tutabilmesini sağlayan şey budur; sunucunun elinde anlaşmazlığa düşülecek bir “geçerli çalışma alanı” yoktur — ve bu, yalıtımın bağlantı başına değil, çağrı başına uygulandığı anlamına gelir.
Bu, yirmi bir üyeli — on beş paket artı altı uygulama — tek bir çözümlenmiş lockfile’a sahip bir Dart workspace’tir; çalışma alanı kökü masaüstü+web uygulamasıdır (control_center). Altı uygulama şunlardır: headless sunucu ikilisi (cc_server), headless filo yürütücüsü (cc_worker), telefon PWA’sı (cc_remote), WebSocket relay broker’ı (cc_signaling_server), Widgetbook galerisi (cc_gallery) ve genel demo barındırıcısı (cc_demo_server). On beş paket şunlardır: cc_ui (tasarım sistemi), cc_domain (saf Dart paylaşılan çekirdek: varlıklar, değer nesneleri, portlar, olaylar ve her özelliğin alan katmanı), cc_harness (yerleşik ajan çekirdeği: saf Dart bir ajan döngüsü — mesajlar, sağlayıcı portu, araçlar, sıkıştırma, yönlendirme, kancalar, alt ajanlar — dart:io olmadan ve başka bir cc_* bağımlılığı olmadan), cc_harness_runtime (yerleşik harness çalışma zamanı adaptörü: yalnızca VM akış sağlayıcıları, OAuth/PKCE kimlik bilgisi aracılığı, kimlik bilgisi depoları ve genel araç kümesi — harici CLI kurulu olmadan ajanları çalıştırır), cc_rpc (istemci taşıma katmanları), cc_host (sunucu çekirdeği), cc_data (uzak depolar), cc_persistence (sunucu tarafı DB), cc_server_core (uygulama-sunucu kompozisyonu + kimlik/mevcudiyet/filo/eval çalışma zamanı), cc_infra (yalnızca VM adaptörleri), cc_mcp (MCP araçları), cc_mcp_client (harici MCP sunucuları için istemci; araçlarını yerel kayıt defterine köprüler), cc_markdown (depo içi markdown motoru), cc_natives (FFI yaprağı) ve system_audio_capture. Tam istemci matrisi ve neyin nerede çalıştığı için bkz. Dağıtım ve istemciler.
Bağımlılık kuralı
Bölüm başlığı “Bağımlılık kuralı”Presentation → Application/Providers → Domain ← Infrastructure- Alan katmanı: saf Dart varlıklar, değer nesneleri, depo arayüzleri, portlar ve alan hizmetleri. Hiç altyapı import’u yok (dio, drift ya da ağ modeli yok).
- Sunum katmanı: ekranlar, widget’lar, notifier’lar. Doğrudan drift/DAO/veri katmanı erişimi yok; her şey Riverpod provider’ları → depolar üzerinden gider.
- Altyapı katmanı: alan portlarının ve depo arayüzlerinin somut uygulamaları. Alana bağımlıdır, tersi asla.
Özellik yapısı
Bölüm başlığı “Özellik yapısı”Tam özellik yapısı şudur:
feature_name/├── data/ # Repository implementations, data sources, services, DTOs, mappers├── domain/ # Entities, repository interfaces, ports, use cases├── presentation/ # Screens, widgets, notifiers└── providers/ # Riverpod providerslib/features/ altında dördünü birden gerçekten taşıyan çok az özellik var ve bu boşluklar tesadüfi değil, bilgilendiricidir. Paylaşılan çekirdek her özelliğin domain/ dizinini cc_domain içine aldı ve ince istemci hiçbir depo uygulaması barındırmıyor; bu yüzden lib/ altındaki çoğu özellik presentation/ artı providers/ ve başka bir şey değil. İstemcide ayakta kalan bir data/ veya domain/ dizini, gerçekten istemci tarafı olan bir şeyi işaret eder.
Bilinmeye değer iki adlandırılmış istisna var:
mcpyalnızca provider’lardan oluşur. Ayarları ve durum arayüzüsettings/altında yaşar ve araç yüzeyinin kendisi bir özellik klasörü değil, bir pakettir (cc_mcp).orchestrationveplan_studioyalnızcapresentation/veproviders/taşır.
guardrails özelliği alan katmanını kök uygulama yerine cc_domain/features/guardrails/ içine koyar, çünkü bunu sunucu uygular: bir kural, bir kapsamda bir ActionClass’ı (ya da bir kabuk commandPrefix’ini) bir karara — allow, prompt ya da deny — eşler; kurallar çalışma alanı başına saklanır ve yönetilen her ajan eylemi çalışmadan önce politika zincirinden çözümlenir. Çözümleme space > agent > workspace > mode preset > built-in default sırasını izler ve eşleşen kurala sahip ilk kapsam karar verir; en kısıtlayıcı olma yalnızca tek bir kapsam içinde ve bir eylemin bildirdiği birkaç sınıfı birleştirmede beraberliği bozar. Operatöre bakan yüzey, /workspaces/<id>/settings/workspace/permissions adresindeki Ayarlar → Çalışma alanı → Ajan izinleri’dir. Model için bkz. Korumalar.
Paylaşılan çekirdek
Bölüm başlığı “Paylaşılan çekirdek”packages/cc_domain/lib/core/domain/, 3+ özellik arasında paylaşılan varlıkları ve depoları barındırır. Sıfır altyapı bağımlılığıyla saf Dart’tır; hem Flutter istemcisinin hem de Flutter’sız sunucu ikilisinin aynı tanımları import etmesini sağlayan da budur:
- Çekirdek varlıklar:
Agent,AgentRunLog,Workspace,Repo,ReviewSpaceAssociation - Kimlik ve çok oyunculu:
User,Principal(sealedUserPrincipal|AgentPrincipal),WorkspaceMember,WorkspaceRole - Bellek varlıkları:
MemoryFact,MemoryPolicy,AgentWorkingMemory,MemoryAccessGrant - Paylaşılan değer nesneleri:
AgentSkills,AgentRole,Mode,SandboxBackend,SandboxSpec,RunCost - Paylaşılan portlar:
SandboxPort,CredentialBrokerPort,WorkspaceFilesystemPort,GitRepoInspectorPort,EmbeddingPort - Alan hizmetleri:
MemoryAccessPolicy,ActivityLogger,MentionResolver,RunLivenessClassifier DomainEventBus+ olay türleri (çalışma alanı/ajan, PR/inceleme, mesajlaşma, biletleme, pipeline, orkestrasyon, bellek, takvim/toplantılar, kimlik ve üyelik, gözlemlenebilirlik)
Kimlik ve çok oyunculu
Bölüm başlığı “Kimlik ve çok oyunculu”Control Center çok kullanıcılıdır: insanlar ve ajanlar, bir Principal ile birleştirilen eşit aktörlerdir. Bir çalışma alanı üyesi, bir Workspace’e bir WorkspaceRole ile bağlanan bir User’dır (owner/admin/member/viewer/guest). Erişim sınırı üyeliktir — bir eşleştirme anahtarı değil — ve depo başına izinler, çalışma alanı üyeliğinin forge’dan daha fazla yetki vermesini engeller. Yeni bir sunucuda oluşturulan ilk kullanıcı kurulumun sahibidir; diğerleri davetle ya da OIDC ile katılır. Bir çalışma alanı oluşturmak, o kullanıcıyı owner olarak damgalar.
Gerçek zamanlı işbirliği bir CRDT değil, yetkili sunucu + alan başına son yazan kazanır (LWW) yaklaşımıdır. Mevcudiyet (kim burada, nerede, imleçler, yazma, bir ajanın canlı durumu ve çalışma maliyeti) asla kalıcılaştırılmayan ayrı bir geçici hattır; kalıcı durum ise iyimser değişiklik + sunucu yeniden temellendirmesi + LWW omurgası üzerinde ilerler. İnsanlar ve ajanlar tek bir kadroyu paylaşır. Tek bir insan operatör varken mevcudiyet hattı boş durur ve hiçbir kadro arayüzü görünmez — çok oyunculu olmak yalnız kullanıcıya hiçbir maliyet çıkarmaz.
Portlar ve adaptörler
Bölüm başlığı “Portlar ve adaptörler”Portlar, alan katmanındaki soyut arayüzlerdir. Adaptörler, altyapıdaki somut uygulamalardır. Headless sunucu verinin sahibi olduğu ve Flutter’sız derlenmesi gerektiği için neredeyse her adaptör yalnızca VM’e özeldir ve packages/cc_infra/lib/src/ altında, ilgi alanına göre düzenlenmiş olarak yaşar:
| Port | Adaptör konumu |
|---|---|
SandboxPort |
cc_infra/lib/src/sandboxing/ (native_sandbox_adapter.dart) |
CredentialBrokerPort |
cc_infra/lib/src/sandboxing/ (env_credential_broker.dart, github_fine_grained_broker.dart) |
GitRepoInspectorPort |
cc_infra/lib/src/git/ (git_repo_inspector.dart) |
EmbeddingPort |
cc_infra/lib/src/embedding/ (embedding_service.dart) |
NotificationPort |
lib/core/notifications/ (istemci tarafı: sunucu olaylarını yerel olarak işler) |
AgentBackend |
cc_infra/lib/src/dispatch/backends/ (acp_backend.dart, harness_backend.dart, cli_backends.dart) |
TicketProviderPort |
cc_infra/lib/src/tickets/linear/ (LinearTicketAdapter). Jira, ClickUp ve GitHub Issues, cc_infra/lib/src/tickets/sync/ içindeki bilet eşitleme adaptörleridir; yerel biletler TicketProviderPort olmadan çalışma alanı veritabanında yaşar. |
PipelineEnginePort |
cc_domain features/pipelines/domain/services/ (saf Dart bir alan hizmeti — hiçbir altyapıya ihtiyaç duymaz) |
Uygulamanın kompozisyon kökü (di/providers.dart), istemci tarafı portları Riverpod provider’ları aracılığıyla uygulamalara bağlar; sunucu ise adaptörlerini cc_server_core içinde bir araya getirir.
Durum yönetimi
Bölüm başlığı “Durum yönetimi”Tüm istemci durumu için Riverpod:
- Değiştirilebilir durum için
Notifier<T>veAsyncNotifier<T> - Asenkron veri için
FutureProvider<T> - Listeler ve canlı satırlar, RPC abonelikleri üzerinden
AsyncValue<List<T>>olarak gelir — sunucu içeride Drift.watch()kullanır; hiçbir istemci Drift açmaz - MCP araçları bağımlılıklarını tipli kurucu parametreleri olarak alır, asla
Refolarak değil
Veritabanı
Bölüm başlığı “Veritabanı”İki veritabanına bölünmüş Drift (SQLite); ikisi de cc_persistence içinde:
GlobalDatabase(<dataDir>/global.db) — gerçekten sunucu geneli olan az sayıda tablo: çalışma alanı kaydı, kullanıcılar/tercihler/eşleştirilmiş aygıtlar, SSO bağlantıları, kurulum geneli sunucu ayarları, yönetilen eylem politikaları (çalışma alanı korumalarını yalnızca sıkılaştırabilen kurulum geneli bir sınırlama), kullanıcı başına haber akışı, filo kuyruğu (worker’lar/işler/yerleştirme günlüğü), kimlik doğrulama öncesiworkspace_routesdizini ve kurulum meta verileri. Önyükleme yalnızca bu dosyayı açar, böylece çalışma alanları ne kadar geçmiş biriktirirse biriktirsin başlangıç maliyeti sabit kalır. Buraya bir tablo eklemek, bir ratchet testinin gerekçelendirmenizi zorunlu kıldığı bir yalıtım kararıdır.WorkspaceDatabase(<dataDir>/<workspaceId>/workspace.db) — her çalışma alanı için bir dizin; içinde bir SQLite dosyası artı yalnızca o çalışma alanına ait olan her şeyi barındırır, böylece bir konuşmanın worktree’leri ve ajan dosyaları veritabanının yanında durur ve onunla birlikte silinir. Neredeyse her şey burada yaşar: ajanlar, alanlar, biletler, bellek, pipeline’lar, toplantılar, kod grafiği, incelemeler, depolar. Dosyalar ilk dokunuştaWorkspaceDatabaseManager.of(workspaceId)üzerinden tembelce açılır ve o anda kendiquick_check, FTS/tetikleyici kurulumu vevector_initişlemlerinin bedelini öder.
Her iki yarı da sıkıştırılmış bir v1 temelinden başladı — onCreate geçerli şemayı oluşturur — ve göç adımları yalnızca dağıtılmış bir veritabanının ileriye taşınması gerektiğinde eklenir.
Bir çalışma alanı tek bir dosya olduğundan, bir çalışma alanını dışa aktarmak tablo tablo bir döküm değil, tek bir VACUUM INTO deyimidir (workspace.export); workspace.import böyle bir dosyayı sahiplenir ve gömülü workspace_metayı kullanarak kendi dosyasının yeniden mi sahiplenildiğini yoksa başka bir kurulumdan mı geldiğini ayırt eder.
Her iki veritabanının sahibi de cc_persistencetır ve yalnızca cc_server tarafından açılır; hiçbir istemci DB açmaz — her istemci RPC üzerinden okur/yazar. Tablolar packages/cc_persistence/lib/database/tables/ içinde, DAO’lar .../daos/ içinde tanımlanır; alan varlıkları saf Dart’tır, Drift tablo sınıflarından ayrıdır ve eşleme veri katmanlarında yapılır. FTS5 tam metin aramasını, sqlite_vector ise gömme vektörlerini işler; ikisi de yalnızca sunucuda çalışır; her iki veritabanı da WAL günlük kipinde PRAGMA foreign_keys=ON çalıştırır.
Yönlendirme
Bölüm başlığı “Yönlendirme”go_router; geri kalan her şeyi belirleyen tek bir olguyla: uygulama içindeki her hedef, /workspaces/:workspaceId/… biçiminde çalışma alanı öneki taşır. URL’deki çalışma alanı kimliği, etkin çalışma alanı için tek doğruluk kaynağıdır — activeWorkspaceIdProvider rotadan sürülür, tersi değil — bu yüzden önekli bir rota dışında çalışma alanı kapsamlı veriyi okumak, kimliğini alacak bir yer bulamaz.
router/routes.dartiçindeki, her biri çalışma alanı kimliğini ilk argümanı olarak alan rota kurucu fonksiyonları, dizge sabitleri değil- Bağlam öncesi dört rota öneksizdir:
/splash,/onboarding,/signed-outve/workspaces(seçici) ShellRouteuygulama kabuğunu sarar; bu dört yüzey onun dışında tam ekran işlenir- Kimlik doğrulama koruması: tamamlanmamış kurulum
/onboardingadresine, forge kimlik bilgisi kaybolmuş tamamlanmış bir kurulum ise/signed-outadresine gider. Tamamlanmış olmak, oturum açmış kullanıcı için en az bir forge’un bağlı olması ve en az bir çalışma alanının var olması demektir — özellikle GitHub değil; ayrıcaghCLI bir kimlik doğrulama yöntemi değildir features/auth/providers/içindeki onboarding kapısı
Ağ (sunucu tarafı)
Bölüm başlığı “Ağ (sunucu tarafı)”Tüm harici HTTP sunucuda yaşar. İstemciler GitHub’a, Linear’a ya da Google’a asla doğrudan bağlanmaz — sunucu RPC işlemlerini çağırırlar ve uzak medya bile asıl kaynaktan değil, sunucunun /proxy/media uç noktası üzerinden getirilir.
packages/cc_infra/lib/src/içindeki dio istemcileri:GitHubApiClient,GitHubPrClient,GitHubContentClient,GitHubGraphQLClient,GitLabApiClient,BitbucketApiClient,LinearGraphQlClientveGoogleCalendarApiClient- Interceptor’lar aracılığıyla kimlik doğrulama token’ı enjeksiyonu; buna 401’de token’ları yenileyen,
cc_server_coreiçindeki hesap başına bir Google OAuth interceptor’ı da dahildir - Tüm hatalar
packages/cc_infra/lib/src/network/error_mapper.dartüzerinden eşlenir →cc_domainiçinde tanımlı tipliAppExceptionalt sınıflarına
Güvenlik
Bölüm başlığı “Güvenlik”Önemli kimlik bilgileri sunucuda durur: ajan başlatmaları için GitHub ve biletleme token’ları, LLM sağlayıcı anahtarları ve OAuth token’ları, hepsi sunucunun veri dizini altında ya da ortamında yaşar. İstemci anahtarlığında kalanlarsa kendi aygıt kimlik bilgisi ve uzak bir sunucu için eşleştirme anahtarıdır.
- Sırlar
flutter_secure_storageiçinde (macOS anahtarlığı, Windows kimlik bilgisi yöneticisi, Linux libsecret) — istemcinin aygıt kimlik bilgisi ve uzak eşleştirme anahtarları, sağlayıcı token’ları değil - Yalnızca hassas olmayan tercihler için
shared_preferences(tema, yazı tipi, yerleşim) - Eski kurulumlardan kalan sağlayıcı sırları,
features/auth/providers/credential_migration.darttarafından bir kez sunucuya devredilir ve ardından istemci anahtarlığından silinir
Mimari kısıtlarının uygulanması
Bölüm başlığı “Mimari kısıtlarının uygulanması”Mimari kısıtlar, test/core/architecture_constraints_test.dart tarafından doğrulanır; bu test, bağımlılık kuralları ihlal edilirse başarısız olur.
İlgili kavramlar
Bölüm başlığı “İlgili kavramlar”- Çalışma alanları ve yalıtım: yalıtım nasıl uygulanır
- Ajan gönderim yaşam döngüsü: gönderim akışı
- Alan olayları: özellikler arası iletişim
- Dağıtım ve istemciler: ince istemci sunucu modeli ve istemci matrisi