39a7305c97
* refactor(opencode): migrate ModelCache and Config to effect-native services Remove legacy async wrapper functions from Config module and convert ModelCache from a stateful namespace with module-level Maps into a proper Effect service with Context/Layer semantics. Key changes: - Delete Config's `makeRuntime`-based async wrappers (get, getGlobal, update, warnings, etc.) — all callsites now use `Config.Service.use(...)` through AppRuntime - Rewrite ModelCache as an Effect service with HttpClient dependency injection, replacing imperative Map-based caching with Effect-native Ref cells and TTL logic - Convert KiloSessions.init and KilocodeBootstrap.init into proper Effect services with Layer-based dependency injection - Wire ModelCache.Service into AppLayer, ProviderAuth, ModelsDev, and HTTP API handler layers - Update Permission.layer to depend on Config.Service directly instead of calling Config async wrappers - Add new test files for KiloSessions and ModelCache Effect integration - Remove stale Config.get spyOn mocks from tests that no longer need them (experimental-session-list, recall) - Fix indexing-auth to use typed IndexingConfig parameter instead of untyped record access * fix(model-cache): resolve race conditions in concurrent fetch and cache invalidation Introduce versioned cache cells with proper key derivation to prevent stale responses from overwriting fresher data during concurrent fetches. - Add version tracking to detect and discard outdated fetch results - Derive cache keys from provider-specific options (baseURL, token, apiKey) to isolate concurrent requests with different credentials - Make ModelCache.clear async to properly await invalidation across layers - Update OrganizationDeps.clear signature to allow Promise<void> return - Add concurrency and ordering tests for fetch/refresh race scenarios - Rename local variable from `state` to `entry` in kilo-sessions sync loop * chore(opencode): remove duplicate imports and fix test layer composition Remove duplicate `AppRuntime` imports introduced during merge and update kilo-sessions tests to use Effect-native Auth service instead of static module calls. - Remove duplicate `AppRuntime` import in index.ts and instance.ts - Add Auth.defaultLayer to test layer helper - Refactor test to yield Auth.Service and use instance methods - Reorder Effect.provide/Effect.ensuring for correct resource cleanup * style(opencode): normalize kilocode_change marker comments to block format Standardize inline `// kilocode_change` annotations across source and test files to use consistent `// kilocode_change start` / `// kilocode_change end` block delimiters, improving readability and grep-ability of custom modifications.
136 lines
4.6 KiB
TypeScript
136 lines
4.6 KiB
TypeScript
import { Layer, ManagedRuntime } from "effect"
|
|
import { attach } from "./run-service"
|
|
import * as Observability from "@opencode-ai/core/effect/observability"
|
|
|
|
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
|
import { Bus } from "@/bus"
|
|
import { Auth } from "@/auth"
|
|
import { Account } from "@/account/account"
|
|
import { Config } from "@/config/config"
|
|
import { Git } from "@/git"
|
|
import { Ripgrep } from "@/file/ripgrep"
|
|
import { File } from "@/file"
|
|
import { FileWatcher } from "@/file/watcher"
|
|
import { Storage } from "@/storage/storage"
|
|
import { Snapshot } from "@/snapshot"
|
|
import { Plugin } from "@/plugin"
|
|
import { ModelsDev } from "@/provider/models"
|
|
import { ModelCache } from "@/provider/model-cache" // kilocode_change
|
|
import { Provider } from "@/provider/provider"
|
|
import { ProviderAuth } from "@/provider/auth"
|
|
import { Agent } from "@/agent/agent"
|
|
import { Skill } from "@/skill"
|
|
import { Discovery } from "@/skill/discovery"
|
|
import { Question } from "@/question"
|
|
import { Permission } from "@/permission"
|
|
import { Todo } from "@/session/todo"
|
|
import { Session } from "@/session/session"
|
|
import { SessionStatus } from "@/session/status"
|
|
import { SessionRunState } from "@/session/run-state"
|
|
import { SessionProcessor } from "@/session/processor"
|
|
import { SessionCompaction } from "@/session/compaction"
|
|
import { SessionRevert } from "@/session/revert"
|
|
import { SessionSummary } from "@/session/summary"
|
|
import { SessionPrompt } from "@/session/prompt"
|
|
import { Instruction } from "@/session/instruction"
|
|
import { LLM } from "@/session/llm"
|
|
import { LSP } from "@/lsp/lsp"
|
|
import { MCP } from "@/mcp"
|
|
import { McpAuth } from "@/mcp/auth"
|
|
import { Command } from "@/command"
|
|
import { Truncate } from "@/tool/truncate"
|
|
import { ToolRegistry } from "@/tool/registry"
|
|
import { Format } from "@/format"
|
|
import { InstanceLayer } from "@/project/instance-layer"
|
|
import { Project } from "@/project/project"
|
|
import { Vcs } from "@/project/vcs"
|
|
import { Workspace } from "@/control-plane/workspace"
|
|
import { Worktree } from "@/worktree"
|
|
import { Pty } from "@/pty"
|
|
import { PtyTicket } from "@/pty/ticket"
|
|
import { Installation } from "@/installation"
|
|
import { ShareNext } from "@/share/share-next"
|
|
import { SessionShare } from "@/share/session"
|
|
import { SyncEvent } from "@/sync"
|
|
import { Npm } from "@opencode-ai/core/npm"
|
|
import { memoMap } from "@opencode-ai/core/effect/memo-map"
|
|
|
|
export const AppLayer = Layer.mergeAll(
|
|
Npm.defaultLayer,
|
|
AppFileSystem.defaultLayer,
|
|
Bus.defaultLayer,
|
|
Auth.defaultLayer,
|
|
Account.defaultLayer,
|
|
Config.defaultLayer,
|
|
Git.defaultLayer,
|
|
Ripgrep.defaultLayer,
|
|
File.defaultLayer,
|
|
FileWatcher.defaultLayer,
|
|
Storage.defaultLayer,
|
|
Snapshot.defaultLayer,
|
|
Plugin.defaultLayer,
|
|
ModelCache.defaultLayer, // kilocode_change
|
|
ModelsDev.defaultLayer,
|
|
Provider.defaultLayer,
|
|
ProviderAuth.defaultLayer,
|
|
Agent.defaultLayer,
|
|
Skill.defaultLayer,
|
|
Discovery.defaultLayer,
|
|
Question.defaultLayer,
|
|
Permission.defaultLayer,
|
|
Todo.defaultLayer,
|
|
Session.defaultLayer,
|
|
SessionStatus.defaultLayer,
|
|
SessionRunState.defaultLayer,
|
|
SessionProcessor.defaultLayer,
|
|
SessionCompaction.defaultLayer,
|
|
SessionRevert.defaultLayer,
|
|
SessionSummary.defaultLayer,
|
|
SessionPrompt.defaultLayer,
|
|
Instruction.defaultLayer,
|
|
LLM.defaultLayer,
|
|
LSP.defaultLayer,
|
|
MCP.defaultLayer,
|
|
McpAuth.defaultLayer,
|
|
Command.defaultLayer,
|
|
Truncate.defaultLayer,
|
|
ToolRegistry.defaultLayer,
|
|
Format.defaultLayer,
|
|
Project.defaultLayer,
|
|
Vcs.defaultLayer,
|
|
Workspace.defaultLayer,
|
|
Worktree.appLayer,
|
|
Pty.defaultLayer,
|
|
PtyTicket.defaultLayer,
|
|
Installation.defaultLayer,
|
|
ShareNext.defaultLayer,
|
|
SessionShare.defaultLayer,
|
|
SyncEvent.defaultLayer,
|
|
).pipe(Layer.provideMerge(InstanceLayer.layer), Layer.provideMerge(Observability.layer))
|
|
|
|
const rt = ManagedRuntime.make(AppLayer, { memoMap })
|
|
type Runtime = Pick<typeof rt, "runSync" | "runPromise" | "runPromiseExit" | "runFork" | "runCallback" | "dispose">
|
|
|
|
/** Services provided by AppRuntime — i.e. what an Effect run via AppRuntime.runPromise can yield. */
|
|
export type AppServices = ManagedRuntime.ManagedRuntime.Services<typeof rt>
|
|
const wrap = (effect: Parameters<typeof rt.runSync>[0]) => attach(effect as never) as never
|
|
|
|
export const AppRuntime: Runtime = {
|
|
runSync(effect) {
|
|
return rt.runSync(wrap(effect))
|
|
},
|
|
runPromise(effect, options) {
|
|
return rt.runPromise(wrap(effect), options)
|
|
},
|
|
runPromiseExit(effect, options) {
|
|
return rt.runPromiseExit(wrap(effect), options)
|
|
},
|
|
runFork(effect) {
|
|
return rt.runFork(wrap(effect))
|
|
},
|
|
runCallback(effect) {
|
|
return rt.runCallback(wrap(effect))
|
|
},
|
|
dispose: () => rt.dispose(),
|
|
}
|