Files
kilocode/packages/opencode/src/effect/app-runtime.ts
T
Johnny Amancio 3c20e6a6d6 fix: let the listener reuse the app services instead of rebuilding them
AppLayer was still missing EventV2, ProjectV2 and ProjectCopy, so
createListenerRoutes compensated by providing the whole application graph.
KiloListener wraps the routes in Layer.fresh, so that build ran against a
new memo map and stood up a second copy of every service, including the
database, plugins and the event bus.

Adding the three nodes to AppLayer lets the listener drop that provide, so
it resolves the same instances the rest of the process uses.

tool-input-delta and -end now just return. The guard only needs to not call
ensureToolCall; reading the part back cost a query per streamed chunk and
the result was discarded.
2026-07-30 20:33:53 +02:00

162 lines
5.8 KiB
TypeScript

import { Layer, ManagedRuntime } from "effect"
import { attach } from "./run-service"
import * as Observability from "@opencode-ai/core/observability"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { Database } from "@opencode-ai/core/database/database"
import { Credential } from "@opencode-ai/core/credential" // kilocode_change
import { Auth } from "@/auth"
import { Account } from "@/account/account"
import { Config } from "@/config/config"
import { Git } from "@/git"
import { Ripgrep } from "@opencode-ai/core/ripgrep"
import { Storage } from "@/storage/storage"
import { Snapshot } from "@/snapshot"
import { Plugin } from "@/plugin"
import { ModelsDev } from "@opencode-ai/core/models-dev"
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 { InstanceStore } from "@/project/instance-store"
import { Project } from "@/project/project"
import { Vcs } from "@/project/vcs"
import { Workspace } from "@/control-plane/workspace"
import { Worktree } from "@/worktree"
import { Installation } from "@/installation"
import { MemoryService } from "@kilocode/kilo-memory/effect/service" // kilocode_change
import { ShareNext } from "@/share/share-next"
import { SessionShare } from "@/share/session"
import { Npm } from "@opencode-ai/core/npm"
import { memoMap } from "@opencode-ai/core/effect/memo-map"
import { BackgroundJob } from "@/background/job"
import { RuntimeFlags } from "@/effect/runtime-flags"
// kilocode_change start
import { Notebook } from "@/kilocode/notebook/service"
import { AgentManager } from "@/kilocode/agent-manager/service"
// kilocode_change end
import { EventV2Bridge } from "@/event-v2-bridge"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { AppNodeBuilderV1 } from "./app-node-builder-v1"
import { SessionProjector } from "@opencode-ai/core/session/projector"
import { EventV2 } from "@opencode-ai/core/event" // kilocode_change
import { ProjectV2 } from "@opencode-ai/core/project" // kilocode_change
import { ProjectCopy } from "@opencode-ai/core/project/copy" // kilocode_change
import { MoveSession } from "@opencode-ai/core/control-plane/move-session" // kilocode_change
import { PtyTicket } from "@opencode-ai/core/pty/ticket" // kilocode_change
// kilocode_change start - retain Kilo runtime services in the upstream node graph
const memory = LayerNode.make({ service: MemoryService.Service, layer: MemoryService.layer, deps: [] })
const kilo = LayerNode.group([Credential.node, ModelCache.node, AgentManager.node, Notebook.node, memory])
// kilocode_change end
export const AppLayer = AppNodeBuilderV1.build(
LayerNode.group([
kilo, // kilocode_change
Npm.node,
FSUtil.node,
Database.node,
Auth.node,
Account.node,
Config.node,
Git.node,
Storage.node,
Snapshot.node,
Plugin.node,
ModelsDev.node,
Provider.node,
ProviderAuth.node,
Agent.node,
Skill.node,
Discovery.node,
Question.node,
Permission.node,
Todo.node,
Session.node,
SessionProjector.node,
SessionStatus.node,
BackgroundJob.node,
RuntimeFlags.node,
EventV2Bridge.node,
SessionRunState.node,
SessionProcessor.node,
SessionCompaction.node,
SessionRevert.node,
SessionSummary.node,
SessionPrompt.node,
Instruction.node,
LLM.node,
LSP.node,
MCP.node,
McpAuth.node,
Command.node,
Truncate.node,
ToolRegistry.node,
Format.node,
InstanceStore.node,
Project.node,
Vcs.node,
Workspace.node,
Worktree.node,
Installation.node,
ShareNext.node,
SessionShare.node,
// kilocode_change start - the app runtime must provide these; the v2 handlers resolve them
// when their layer is built, outside the server's own layer list
EventV2.node,
ProjectV2.node,
ProjectCopy.node,
MoveSession.node,
PtyTicket.node,
// kilocode_change end
]),
).pipe(Layer.provideMerge(AppNodeBuilderV1.build(Ripgrep.node)), 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(),
}