chore: merge dev into v2 (#34788)

Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
Co-authored-by: Kit Langton <kit.langton@gmail.com>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Affan Ali <93028901+affanali2k3@users.noreply.github.com>
Co-authored-by: affanali2k3 <affanalikhanxx@gmail.com>
Co-authored-by: Frank <frank@anoma.ly>
Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: 𝓛𝓲𝓽𝓽𝓵𝓮 𝓕𝓻𝓪𝓷𝓴 <little-frank@opencord.local>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Jay V <air@live.ca>
Co-authored-by: Dax Raad <d@ironbay.co>
Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com>
Co-authored-by: OpeOginni <107570612+OpeOginni@users.noreply.github.com>
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
Co-authored-by: Ben Guthrie <benjee.012@gmail.com>
Co-authored-by: Dax <mail@thdxr.com>
Co-authored-by: Filip <34747899+neriousy@users.noreply.github.com>
Co-authored-by: Max Anderson <max.a.anderson95@gmail.com>
Co-authored-by: Brendan Allan <git@brendonovich.dev>
Co-authored-by: Jack <jack@anoma.ly>
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
Co-authored-by: Dustin Deus <deusdustin@gmail.com>
Co-authored-by: starptech <starptech@starptechs-MBP.fritz.box>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com>
Co-authored-by: Jay <53023+jayair@users.noreply.github.com>
Co-authored-by: runvip <164729189+runvip@users.noreply.github.com>
Co-authored-by: opencode <opencode@sst.dev>
Co-authored-by: Julian Coy <julian@ex-machina.co>
Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com>
This commit is contained in:
James Long
2026-07-01 17:12:00 -04:00
committed by GitHub
co-authored by Brendan Allan Kit Langton opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Affan Ali affanali2k3 Frank opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> 𝓛𝓲𝓽𝓽𝓵𝓮 𝓕𝓻𝓪𝓷𝓴 Aiden Cline Jay V Dax Raad Aarav Sareen OpeOginni Luke Parker Ben Guthrie Dax Filip Max Anderson Brendan Allan Jack Shoubhit Dash Dustin Deus starptech Aiden Cline usrnk1 Jay runvip opencode Julian Coy Vladimir Glafirov
parent 932a40cfd9
commit 8c94e9005f
590 changed files with 15772 additions and 5530 deletions
+1 -3
View File
@@ -186,7 +186,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Ac
export const use = serviceUse(Service)
export const layer: Layer.Layer<Service, never, AccountRepo.Service | HttpClient.HttpClient> = Layer.effect(
const layer: Layer.Layer<Service, never, AccountRepo.Service | HttpClient.HttpClient> = Layer.effect(
Service,
Effect.gen(function* () {
const repo = yield* AccountRepo.Service
@@ -456,8 +456,6 @@ export const layer: Layer.Layer<Service, never, AccountRepo.Service | HttpClient
}),
)
export const defaultLayer = layer.pipe(Layer.provide(AccountRepo.defaultLayer), Layer.provide(FetchHttpClient.layer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [AccountRepo.node, httpClient] })
export * as Account from "./account"
+1 -3
View File
@@ -39,7 +39,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Ac
export const use = serviceUse(Service)
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const { db } = yield* Database.Service
@@ -166,8 +166,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(Database.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [Database.node] })
export * as AccountRepo from "./repo"
+10 -8
View File
@@ -1,7 +1,9 @@
import { Agent } from "@/agent/agent"
import { Command } from "@/command"
import { InstanceRef } from "@/effect/instance-ref"
import { InstanceBootstrap } from "@/project/bootstrap"
import { InstanceStore } from "@/project/instance-store"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { ModelV2 } from "@opencode-ai/core/model"
import { Provider } from "@/provider/provider"
@@ -139,7 +141,7 @@ export const loaderLayer = Layer.effect(
}),
)
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const loader = yield* Loader
@@ -199,12 +201,12 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(loaderLayer),
Layer.provide(Provider.defaultLayer),
Layer.provide(Agent.defaultLayer),
Layer.provide(Command.defaultLayer),
Layer.provide(InstanceStore.defaultLayer),
)
export const loaderNode = LayerNode.make({
service: Loader,
layer: loaderLayer,
deps: [Provider.node, Agent.node, Command.node, InstanceStore.node],
})
export const node = LayerNode.make({ service: Service, layer, deps: [loaderNode] })
export * as Directory from "./directory"
+7 -5
View File
@@ -30,6 +30,7 @@ import {
type SetSessionModeResponse,
} from "@agentclientprotocol/sdk"
import { InstallationVersion } from "@opencode-ai/core/installation/version"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import type { AssistantMessage, Message, OpencodeClient, SessionMessageResponse } from "@opencode-ai/sdk/v2"
import { Context, Effect, Layer, ManagedRuntime } from "effect"
import * as ACPError from "./error"
@@ -569,23 +570,24 @@ export function make(input: {
}
function makeSessionService() {
return ManagedRuntime.make(ACPSession.defaultLayer).runSync(
return ManagedRuntime.make(AppNodeBuilder.build(ACPSession.node)).runSync(
ACPSession.Service.use((service) => Effect.succeed(service)),
)
}
function makeDirectoryService(sdk: OpencodeClient) {
return ManagedRuntime.make(
Directory.layer.pipe(
Layer.provide(
AppNodeBuilder.build(Directory.node, [
[
Directory.loaderNode,
Layer.succeed(
Directory.Loader,
Directory.Loader.of({
load: (directory) => request(() => loadDirectorySnapshot(sdk, directory), "directory"),
}),
),
),
),
],
]),
).runSync(Directory.Service.use((service) => Effect.succeed(service)))
}
+3 -2
View File
@@ -1,5 +1,6 @@
import type { McpServer } from "@agentclientprotocol/sdk"
import type { Message, Part } from "@opencode-ai/sdk/v2"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { ModelV2 } from "@opencode-ai/core/model"
import { Context, Effect, Layer, Ref } from "effect"
@@ -93,7 +94,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/AC
type State = Map<string, Info>
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const sessions = yield* Ref.make<State>(new Map())
@@ -200,7 +201,7 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer
export const node = LayerNode.make({ service: Service, layer, deps: [] })
function makeSession(input: StoreInput): Info {
return {
+13 -6
View File
@@ -1,7 +1,10 @@
import type { AgentSideConnection, Usage } from "@agentclientprotocol/sdk"
import type { AssistantMessage as OpenCodeAssistantMessage, Message } from "@opencode-ai/sdk/v2"
import { InstanceRef } from "@/effect/instance-ref"
import { InstanceBootstrap } from "@/project/bootstrap"
import { InstanceStore } from "@/project/instance-store"
import { makeGlobalNode, Node } from "@opencode-ai/core/effect/app-node"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { ModelV2 } from "@opencode-ai/core/model"
import { Provider } from "@/provider/provider"
@@ -132,7 +135,7 @@ export const contextLimitLoaderLayer = Layer.effect(
}),
)
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const messageLoader = yield* MessageLoader
@@ -223,10 +226,14 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(contextLimitLoaderLayer),
Layer.provide(Provider.defaultLayer),
Layer.provide(InstanceStore.defaultLayer),
)
export const messageLoaderNode = LayerNode.unbound(MessageLoader, Node.tags.values.global)
export const contextLimitLoaderNode = makeGlobalNode({
service: ContextLimitLoader,
layer: contextLimitLoaderLayer,
deps: [Provider.node, InstanceStore.node],
})
export const node = makeGlobalNode({ service: Service, layer, deps: [messageLoaderNode, contextLimitLoaderNode] })
export * as UsageService from "./usage"
+1 -10
View File
@@ -85,7 +85,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Ag
export const use = serviceUse(Service)
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const config = yield* Config.Service
@@ -438,15 +438,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Plugin.defaultLayer),
Layer.provide(Provider.defaultLayer),
Layer.provide(Auth.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(Skill.defaultLayer),
Layer.provide(locationServiceMapLayer),
)
const locationServiceMapNode = LayerNode.make({
service: LocationServiceMap.Service,
layer: locationServiceMapLayer,
+1 -3
View File
@@ -49,7 +49,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/Auth") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fsys = yield* FSUtil.Service
@@ -92,8 +92,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(FSUtil.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [FSUtil.node] })
export * as Auth from "."
+2 -1
View File
@@ -7,8 +7,9 @@ export const ScrapCommand = cmd({
builder: (yargs) => yargs,
async handler() {
const { Project } = await import("@/project/project")
const { AppNodeBuilder } = await import("@opencode-ai/core/effect/app-node-builder")
const { makeRuntime } = await import("@opencode-ai/core/effect/runtime")
const runtime = makeRuntime(Project.Service, Project.defaultLayer)
const runtime = makeRuntime(Project.Service, AppNodeBuilder.build(Project.node))
const list = await runtime.runPromise((project) => project.list())
process.stdout.write(JSON.stringify(list, null, 2) + EOL)
},
+1 -1
View File
@@ -761,7 +761,7 @@ export const McpDebugCommand = effectCmd({
}
if (response.status === 401) {
prompts.log.warn("Server returned 401 Unauthorized")
prompts.log.info("Initial unauthenticated check returned 401, so this server requires OAuth")
// Try to discover OAuth metadata
const oauthConfig = typeof serverConfig.oauth === "object" ? serverConfig.oauth : undefined
+22 -2
View File
@@ -233,11 +233,27 @@ export const RunCommand = effectCmd({
hidden: true,
describe: "cap visible interactive replay to the newest N messages",
})
.option("dangerously-skip-permissions", {
.option("interactive", {
alias: ["i"],
type: "boolean",
describe: "run in direct interactive split-footer mode",
default: false,
})
.option("auto", {
type: "boolean",
describe: "auto-approve permissions that are not explicitly denied (dangerous!)",
default: false,
})
.option("yolo", {
type: "boolean",
hidden: true,
default: false,
})
.option("dangerously-skip-permissions", {
type: "boolean",
hidden: true,
default: false,
})
.option("demo", {
type: "boolean",
default: false,
@@ -255,6 +271,7 @@ export const RunCommand = effectCmd({
yield* Effect.promise(async () => {
const rawMessage = [...args.message, ...(args["--"] || [])].join(" ")
const interactive = args.mini
const auto = args.auto || args.yolo || args["dangerously-skip-permissions"]
const thinking = interactive ? (args.thinking ?? true) : (args.thinking ?? false)
const die = (message: string): never => {
UI.error(message)
@@ -780,7 +797,7 @@ export const RunCommand = effectCmd({
const permission = event.properties
if (permission.sessionID !== sessionID) continue
if (args["dangerously-skip-permissions"]) {
if (auto) {
await client.permission.reply({
requestID: permission.id,
reply: "once",
@@ -981,9 +998,12 @@ export async function runMini(input: MiniCommandInput) {
variant: undefined,
thinking: undefined,
mini: true,
interactive: false,
replay: input.replay ?? true,
"replay-limit": input.replayLimit,
replayLimit: input.replayLimit,
auto: false,
yolo: false,
"dangerously-skip-permissions": false,
dangerouslySkipPermissions: false,
demo: input.demo ?? false,
@@ -8,6 +8,7 @@
// variant and the persisted file.
import path from "path"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { Context, Effect, Layer } from "effect"
import { makeRuntime } from "@/effect/run-service"
import { Global } from "@opencode-ai/core/global"
@@ -135,7 +136,7 @@ function state(value: unknown): ModelState {
}
}
function createLayer(fs = FSUtil.defaultLayer) {
function createLayer(fs = AppNodeBuilder.build(FSUtil.node)) {
return Layer.fresh(
Layer.effect(
Service,
@@ -196,7 +197,7 @@ function createLayer(fs = FSUtil.defaultLayer) {
}
/** @internal Exported for testing. */
export function createVariantRuntime(fs = FSUtil.defaultLayer): VariantRuntime {
export function createVariantRuntime(fs = AppNodeBuilder.build(FSUtil.node)): VariantRuntime {
const runtime = makeRuntime(Service, createLayer(fs))
return {
resolveSavedVariant: (model) => runtime.runPromise((svc) => svc.resolveSavedVariant(model)).catch(() => undefined),
+18 -1
View File
@@ -6,11 +6,12 @@ import { fileURLToPath } from "url"
import { UI } from "@/cli/ui"
import { errorMessage } from "@opencode-ai/tui/util/error"
import { withTimeout } from "@/util/timeout"
import { withNetworkOptions, resolveNetworkOptionsNoConfig } from "@/cli/network"
import { withNetworkOptions, resolveNetworkOptionsNoConfig, hasArg } from "@/cli/network"
import { Filesystem } from "@/util/filesystem"
import { OpenCode } from "@opencode-ai/client"
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
import { writeHeapSnapshot } from "v8"
import { ServerAuth } from "@/server/auth"
import { validateSession } from "../tui/validate-session"
import { win32InstallCtrlCGuard } from "@opencode-ai/tui/terminal-win32"
@@ -74,6 +75,21 @@ export const TuiThreadCommand = cmd({
type: "string",
describe: "agent to use",
})
.option("auto", {
type: "boolean",
describe: "auto-approve permissions that are not explicitly denied (dangerous!)",
default: false,
})
.option("yolo", {
type: "boolean",
hidden: true,
default: false,
})
.option("dangerously-skip-permissions", {
type: "boolean",
hidden: true,
default: false,
})
.option("mini", {
type: "boolean",
describe: "start the minimal interactive interface",
@@ -221,6 +237,7 @@ export const TuiThreadCommand = cmd({
model: args.model,
prompt,
fork: args.fork,
auto: args.auto || args.yolo || args["dangerously-skip-permissions"],
},
}),
)
+20 -4
View File
@@ -37,6 +37,22 @@ export type NetworkOptions = InferredOptionTypes<typeof options>
export function withNetworkOptions<T>(yargs: Argv<T>) {
return yargs.options(options)
}
export function hasArg(name: string) {
return networkArgs().some((arg) => arg === name || arg.startsWith(name + "="))
}
function hasBooleanArg(name: string) {
return networkArgs().some(
(arg) => arg === name || arg === name + "=true" || arg === name + "=false" || arg === "--no-" + name.slice(2),
)
}
function networkArgs() {
const separator = process.argv.indexOf("--")
return process.argv.slice(2, separator === -1 ? undefined : separator)
}
export const resolveNetworkOptions = Effect.fn("Cli.resolveNetworkOptions")(function* (args: NetworkOptions) {
const { Config } = yield* Effect.promise(() => import("@/config/config"))
const config = yield* Config.Service.use((cfg) => cfg.getGlobal())
@@ -44,10 +60,10 @@ export const resolveNetworkOptions = Effect.fn("Cli.resolveNetworkOptions")(func
})
export function resolveNetworkOptionsNoConfig(args: NetworkOptions, config?: ConfigV1.Info) {
const portExplicitlySet = process.argv.includes("--port")
const hostnameExplicitlySet = process.argv.includes("--hostname")
const mdnsExplicitlySet = process.argv.includes("--mdns")
const mdnsDomainExplicitlySet = process.argv.includes("--mdns-domain")
const portExplicitlySet = hasArg("--port")
const hostnameExplicitlySet = hasArg("--hostname")
const mdnsExplicitlySet = hasBooleanArg("--mdns")
const mdnsDomainExplicitlySet = hasArg("--mdns-domain")
const mdns = mdnsExplicitlySet ? args.mdns : (config?.server?.mdns ?? args.mdns)
const mdnsDomain = mdnsDomainExplicitlySet ? args["mdns-domain"] : (config?.server?.mdnsDomain ?? args["mdns-domain"])
const port = portExplicitlySet ? args.port : (config?.server?.port ?? args.port)
+2 -1
View File
@@ -1,7 +1,8 @@
import { run as runTui, type TuiInput } from "@opencode-ai/tui"
import { Global } from "@opencode-ai/core/global"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { Effect } from "effect"
export function run(input: TuiInput) {
return runTui(input).pipe(Effect.provide(Global.defaultLayer))
return runTui(input).pipe(Effect.provide(AppNodeBuilder.build(Global.node)))
}
+1 -7
View File
@@ -55,7 +55,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/Command") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const config = yield* Config.Service
@@ -172,12 +172,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Config.defaultLayer),
Layer.provide(MCP.defaultLayer),
Layer.provide(Skill.defaultLayer),
)
export const node = LayerNode.make({ service: Service, layer: layer, deps: [Config.node, MCP.node, Skill.node] })
export * as Command from "."
+1 -11
View File
@@ -172,7 +172,7 @@ function writableGlobal(info: Info) {
return next
}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@@ -671,16 +671,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(EffectFlock.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Env.defaultLayer),
Layer.provide(Auth.defaultLayer),
Layer.provide(Account.defaultLayer),
Layer.provide(Npm.defaultLayer),
Layer.provide(FetchHttpClient.layer),
)
export const node = LayerNode.make({
service: Service,
layer: layer,
+5 -3
View File
@@ -2,6 +2,8 @@ export * as TuiConfig from "./tui"
import path from "path"
import { mergeDeep, unique } from "remeda"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Cause, Context, Effect, Fiber, Layer } from "effect"
import { ConfigParse } from "@/config/parse"
import * as ConfigPaths from "@/config/paths"
@@ -223,7 +225,7 @@ const loadState = Effect.fn("TuiConfig.loadState")(function* (ctx: { directory:
}
})
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const directory = yield* CurrentWorkingDirectory
@@ -257,9 +259,9 @@ export const layer = Layer.effect(
}).pipe(Effect.withSpan("TuiConfig.layer")),
)
export const defaultLayer = layer.pipe(Layer.provide(Npm.defaultLayer), Layer.provide(FSUtil.defaultLayer))
export const node = LayerNode.make({ service: Service, layer, deps: [Npm.node, FSUtil.node] })
const { runPromise } = makeRuntime(Service, defaultLayer)
const { runPromise } = makeRuntime(Service, AppNodeBuilder.build(node))
export async function waitForDependencies() {
await runPromise((svc) => svc.waitForDependencies())
@@ -31,8 +31,8 @@ import { waitEvent } from "./util"
import { WorkspaceRef } from "@/effect/instance-ref"
import { Vcs } from "@/project/vcs"
import { InstanceStore } from "@/project/instance-store"
import { InstanceBootstrap } from "@/project/bootstrap"
import { WorkspaceAdapterRuntime } from "./workspace-adapter-runtime"
import { AppNodeBuilderV1 } from "@/effect/app-node-builder-v1"
import { WorkspaceEvent } from "@opencode-ai/schema/workspace-event"
export const Info = Schema.Struct({
@@ -150,7 +150,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Wo
export const use = serviceUse(Service)
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const auth = yield* Auth.Service
@@ -601,7 +601,7 @@ export const layer = Layer.effect(
}),
fallback: "",
response: "text",
}).pipe(Effect.provide(InstanceStore.defaultLayer.pipe(Layer.provide(InstanceBootstrap.defaultLayer))))
}).pipe(Effect.provide(AppNodeBuilderV1.build(InstanceStore.node)))
: ""
if (sourcePatch) {
@@ -617,7 +617,7 @@ export const layer = Layer.effect(
body: HttpBody.jsonUnsafe({ patch: sourcePatch }),
}),
fallback: { applied: false },
}).pipe(Effect.provide(InstanceStore.defaultLayer.pipe(Layer.provide(InstanceBootstrap.defaultLayer))))
}).pipe(Effect.provide(AppNodeBuilderV1.build(InstanceStore.node)))
}
if (input.workspaceID === null) {
@@ -885,19 +885,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Auth.defaultLayer),
Layer.provide(Session.defaultLayer),
Layer.provide(SessionPrompt.defaultLayer),
Layer.provide(Project.defaultLayer),
Layer.provide(Vcs.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Database.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(FetchHttpClient.layer),
Layer.provide(RuntimeFlags.defaultLayer),
)
const TIMEOUT = 5000
type HistoryEvent = {
@@ -0,0 +1,12 @@
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { InstanceBootstrap } from "@/project/bootstrap"
import { InstanceStore } from "@/project/instance-store"
const bootstrapReplacement = [InstanceStore.bootstrapNode, InstanceBootstrap.node] as const
export function build<A, E>(root: LayerNode.Node<A, E, any>, replacements: LayerNode.Replacements = []) {
return AppNodeBuilder.build(root, replacements.concat([bootstrapReplacement]))
}
export * as AppNodeBuilderV1 from "./app-node-builder-v1"
+56 -53
View File
@@ -38,7 +38,7 @@ 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 { InstanceStore } from "@/project/instance-store"
import { Project } from "@/project/project"
import { Vcs } from "@/project/vcs"
import { Workspace } from "@/control-plane/workspace"
@@ -51,59 +51,62 @@ import { memoMap } from "@opencode-ai/core/effect/memo-map"
import { Job } from "@/job"
import { RuntimeFlags } from "@/effect/runtime-flags"
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"
export const AppLayer = Layer.mergeAll(
Npm.defaultLayer,
FSUtil.defaultLayer,
Database.defaultLayer,
Auth.defaultLayer,
Account.defaultLayer,
Config.defaultLayer,
Git.defaultLayer,
Storage.defaultLayer,
Snapshot.defaultLayer,
Plugin.defaultLayer,
ModelsDev.defaultLayer,
Provider.defaultLayer,
ProviderAuth.defaultLayer,
Agent.defaultLayer,
Skill.defaultLayer,
Discovery.defaultLayer,
Question.defaultLayer,
Permission.defaultLayer,
Todo.defaultLayer,
Session.defaultLayer,
SessionStatus.defaultLayer,
Job.defaultLayer,
RuntimeFlags.defaultLayer,
EventV2Bridge.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,
Installation.defaultLayer,
ShareNext.defaultLayer,
SessionShare.defaultLayer,
).pipe(
Layer.provideMerge(Ripgrep.defaultLayer),
Layer.provideMerge(InstanceLayer.layer),
Layer.provideMerge(Observability.layer),
)
export const AppLayer = AppNodeBuilderV1.build(
LayerNode.group([
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,
Job.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,
]),
).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">
@@ -1,4 +1,6 @@
import { Layer, ManagedRuntime } from "effect"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Plugin } from "@/plugin"
import { LSP } from "@/lsp/lsp"
@@ -10,14 +12,8 @@ import { Config } from "@/config/config"
import * as Observability from "@opencode-ai/core/observability"
import { memoMap } from "@opencode-ai/core/effect/memo-map"
export const BootstrapLayer = Layer.mergeAll(
Config.defaultLayer,
Plugin.defaultLayer,
ShareNext.defaultLayer,
Format.defaultLayer,
LSP.defaultLayer,
Vcs.defaultLayer,
Snapshot.defaultLayer,
export const BootstrapLayer = AppNodeBuilder.build(
LayerNode.group([Config.node, Plugin.node, ShareNext.node, Format.node, LSP.node, Vcs.node, Snapshot.node]),
).pipe(Layer.provide(Observability.layer))
export const BootstrapRuntime = ManagedRuntime.make(BootstrapLayer, { memoMap })
@@ -14,9 +14,9 @@ export type Shape<Fields extends ConfigMap> = {
*/
export type ServiceClass<Self, Id extends string, Service> = Context.ServiceClass<Self, Id, Service> & {
/** Provide already-parsed config, useful in tests. */
readonly layer: (input: Service) => Layer.Layer<Self>
readonly configLayer: (input: Service) => Layer.Layer<Self>
/** Parse config once from the active Effect ConfigProvider and provide the service. */
readonly defaultLayer: Layer.Layer<Self, Config.ConfigError>
readonly layer: Layer.Layer<Self, Config.ConfigError>
}
/**
@@ -35,19 +35,19 @@ export type ServiceClass<Self, Id extends string, Service> = Context.ServiceClas
* },
* ) {}
*
* const live = ServerAuthConfig.defaultLayer
* const test = ServerAuthConfig.layer({ password: Option.some("secret"), username: "kit" })
* const live = ServerAuthConfig.layer
* const test = ServerAuthConfig.configLayer({ password: Option.some("secret"), username: "kit" })
* ```
*/
export const Service =
<Self>() =>
<const Id extends string, const Fields extends ConfigMap>(id: Id, fields: Fields) => {
class ConfigTag extends Context.Service<Self, Shape<Fields>>()(id) {
static layer(input: Shape<Fields>) {
static configLayer(input: Shape<Fields>) {
return Layer.succeed(this, this.of(input))
}
static get defaultLayer() {
static get layer() {
const tag = this
return Layer.effect(
tag,
@@ -57,7 +57,7 @@ export class Service extends ConfigService.Service<Service>()("@opencode/Runtime
export type Info = Context.Service.Shape<typeof Service>
const emptyConfigLayer = Service.defaultLayer.pipe(
const emptyConfigLayer = Service.layer.pipe(
Layer.provide(ConfigProvider.layer(ConfigProvider.fromUnknown({}))),
Layer.orDie,
)
@@ -71,9 +71,7 @@ export const layer = (overrides: Partial<Info> = {}) =>
}),
).pipe(Layer.provide(emptyConfigLayer))
export const defaultLayer = Service.defaultLayer.pipe(Layer.orDie)
export const node = LayerNode.make({ service: Service, layer: defaultLayer, deps: [] })
export const node = LayerNode.make({ service: Service, layer: Service.layer.pipe(Layer.orDie), deps: [] })
export * as RuntimeFlags from "./runtime-flags"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
+1 -3
View File
@@ -16,7 +16,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/En
export const use = serviceUse(Service)
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const state = yield* InstanceState.make<State>(Effect.fn("Env.state")(() => Effect.succeed({ ...process.env })))
@@ -36,8 +36,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer
export const node = LayerNode.make({ service: Service, layer: layer, deps: [] })
export * as Env from "."
+1 -3
View File
@@ -11,7 +11,7 @@ import { Context, Effect, Layer } from "effect"
export class Service extends Context.Service<Service, EventV2.Interface>()("@opencode/EventV2Bridge") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2.Service
@@ -66,8 +66,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(EventV2.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [EventV2.node] })
export * as EventV2Bridge from "./event-v2-bridge"
+1 -7
View File
@@ -28,7 +28,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Fo
export const use = serviceUse(Service)
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const config = yield* Config.Service
@@ -194,12 +194,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Config.defaultLayer),
Layer.provide(AppProcess.defaultLayer),
Layer.provide(RuntimeFlags.defaultLayer),
)
export const node = LayerNode.make({
service: Service,
layer: layer,
+1 -3
View File
@@ -100,7 +100,7 @@ const kind = (code: string): Kind => {
export class Service extends Context.Service<Service, Interface>()("@opencode/Git") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const appProcess = yield* AppProcess.Service
@@ -343,8 +343,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(AppProcess.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [AppProcess.node] })
export * as Git from "."
+1 -3
View File
@@ -56,7 +56,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/Image") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const config = yield* Config.Service
@@ -167,8 +167,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(Config.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [Config.node] })
export * as Image from "./image"
+5 -6
View File
@@ -1,8 +1,9 @@
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { httpClient } from "@opencode-ai/core/effect/app-node-platform"
import { Effect, Layer, Schema, Context, Stream } from "effect"
import { serviceUse } from "@opencode-ai/core/effect/service-use"
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
import { withTransientReadRetry } from "@/util/effect-http-client"
import { errorMessage } from "@/util/error"
import { ChildProcess } from "effect/unstable/process"
@@ -82,7 +83,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/In
export const use = serviceUse(Service)
export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProcess.Service> = Layer.effect(
const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProcess.Service> = Layer.effect(
Service,
Effect.gen(function* () {
const http = yield* HttpClient.HttpClient
@@ -324,14 +325,12 @@ export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProce
}),
)
export const defaultLayer = layer.pipe(Layer.provide(FetchHttpClient.layer), Layer.provide(AppProcess.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [httpClient, AppProcess.node] })
const { runPromise } = makeRuntime(Service, defaultLayer)
const { runPromise } = makeRuntime(Service, AppNodeBuilder.build(node))
export const latest = (...args: Parameters<Interface["latest"]>) => runPromise((s) => s.latest(...args))
export const method = () => runPromise((s) => s.method())
export const upgrade = (...args: Parameters<Interface["upgrade"]>) => runPromise((s) => s.upgrade(...args))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [httpClient, AppProcess.node] })
export * as Installation from "."
-2
View File
@@ -34,8 +34,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer
export const node = LayerNode.make({ service: Service, layer, deps: [] })
export * as Job from "./job"
+1 -7
View File
@@ -135,7 +135,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/LSP") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const config = yield* Config.Service
@@ -496,12 +496,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Config.defaultLayer),
Layer.provide(RuntimeFlags.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
)
export * as Diagnostic from "./diagnostic"
export const node = LayerNode.make({
+1 -3
View File
@@ -56,7 +56,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Mc
export const use = serviceUse(Service)
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@@ -158,8 +158,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(EffectFlock.defaultLayer), Layer.provide(FSUtil.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [FSUtil.node, EffectFlock.node] })
export * as McpAuth from "./auth"
+2 -1
View File
@@ -72,7 +72,8 @@ export function convertTool(mcpTool: MCPToolDef, client: Client, timeout?: numbe
.filter((text) => text.trim())
.join("\n\n") || "MCP tool returned an error",
)
if (result.structuredContent === undefined || result.structuredContent === null) return result
if (result.content.length > 0 || result.structuredContent === undefined || result.structuredContent === null)
return result
return {
...result,
content: [{ type: "text" as const, text: JSON.stringify(result.structuredContent) }],
+2 -12
View File
@@ -194,7 +194,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/MC
export const use = serviceUse(Service)
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
@@ -946,7 +946,7 @@ export const layer = Layer.effect(
const mcpConfig = yield* requireMcpConfig(mcpName)
return yield* createAndStore(mcpName, mcpConfig)
return yield* createAndStore(mcpName, { ...mcpConfig, enabled: true })
})
const removeAuth = Effect.fn("MCP.removeAuth")(function* (mcpName: string) {
@@ -1003,16 +1003,6 @@ export const layer = Layer.effect(
export type AuthStatus = "authenticated" | "expired" | "not_authenticated"
// --- Per-service runtime ---
export const defaultLayer = layer.pipe(
Layer.provide(McpAuth.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(CrossSpawnSpawner.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
)
export const node = LayerNode.make({
service: Service,
layer: layer,
+1 -3
View File
@@ -39,7 +39,7 @@ export function evaluate(permission: string, pattern: string, ...rulesets: Permi
export class Service extends Context.Service<Service, Interface>()("@opencode/Permission") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2Bridge.Service
@@ -213,8 +213,6 @@ export function disabled(tools: string[], ruleset: PermissionV1.Ruleset): Set<st
)
}
export const defaultLayer = layer.pipe(Layer.provide(EventV2Bridge.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [EventV2Bridge.node] })
export * as Permission from "."
+1 -7
View File
@@ -120,7 +120,7 @@ async function applyPlugin(load: PluginLoader.Loaded, input: PluginInput, hooks:
}
}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2Bridge.Service
@@ -305,12 +305,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(RuntimeFlags.defaultLayer),
)
export const node = LayerNode.make({
service: Service,
layer: layer,
+2 -1
View File
@@ -14,6 +14,7 @@ import {
import path from "path"
import { fileURLToPath } from "url"
import { TuiConfig } from "@/config/tui"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { errorData, errorMessage } from "@opencode-ai/tui/util/error"
import { isRecord } from "@opencode-ai/tui/util/record"
import { resolveHostAttentionSoundPaths } from "@/config/tui-host-attention"
@@ -1082,7 +1083,7 @@ async function load(input: {
const flags = await Effect.runPromise(
Effect.gen(function* () {
return yield* RuntimeFlags.Service
}).pipe(Effect.provide(RuntimeFlags.defaultLayer)),
}).pipe(Effect.provide(AppNodeBuilder.build(RuntimeFlags.node))),
)
const pluginOrigins = config.plugin_origins ?? (await TuiConfig.pluginOrigins())
const records = Flag.OPENCODE_PURE ? [] : pluginOrigins
+3 -16
View File
@@ -1,4 +1,4 @@
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { makeGlobalNode } from "@opencode-ai/core/effect/app-node"
import { Plugin } from "../plugin"
import { Format } from "../format"
import { LSP } from "@/lsp/lsp"
@@ -14,7 +14,7 @@ import { Service } from "./bootstrap-service"
export { Service } from "./bootstrap-service"
export type { Interface } from "./bootstrap-service"
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
// Yield each bootstrap dep at layer init so `run` itself has R = never.
@@ -49,20 +49,7 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer: Layer.Layer<Service> = layer.pipe(
Layer.provide([
Config.defaultLayer,
Format.defaultLayer,
LSP.defaultLayer,
Plugin.defaultLayer,
Project.defaultLayer,
ShareNext.defaultLayer,
Snapshot.defaultLayer,
Vcs.defaultLayer,
]),
)
export const node = LayerNode.make({
export const node = makeGlobalNode({
service: Service,
layer: layer,
deps: [Config.node, Format.node, LSP.node, Plugin.node, Project.node, ShareNext.node, Snapshot.node, Vcs.node],
@@ -1,11 +0,0 @@
import { Effect, Layer } from "effect"
import { InstanceStore } from "./instance-store"
export const layer = Layer.unwrap(
Effect.promise(async () => {
const { InstanceBootstrap } = await import("./bootstrap")
return InstanceStore.defaultLayer.pipe(Layer.provide(InstanceBootstrap.defaultLayer))
}),
)
export * as InstanceLayer from "./instance-layer"
@@ -1,4 +1,5 @@
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { makeGlobalNode, Node } from "@opencode-ai/core/effect/app-node"
import { GlobalBus } from "@/bus/global"
import { serviceUse } from "@opencode-ai/core/effect/service-use"
import { WorkspaceContext } from "@/control-plane/workspace-context"
@@ -8,7 +9,6 @@ import { FSUtil } from "@opencode-ai/core/fs-util"
import { Context, Deferred, Duration, Effect, Exit, Layer, Scope } from "effect"
import { type InstanceContext } from "./instance-context"
import { InstanceBootstrap } from "./bootstrap-service"
import { InstanceBootstrap as InstanceBootstrapGraph } from "./bootstrap"
import * as Project from "./project"
export interface LoadInput {
@@ -34,7 +34,7 @@ interface Entry {
readonly deferred: Deferred.Deferred<InstanceContext>
}
export const layer: Layer.Layer<Service, never, Project.Service | InstanceBootstrap.Service> = Layer.effect(
const layer: Layer.Layer<Service, never, Project.Service | InstanceBootstrap.Service> = Layer.effect(
Service,
Effect.gen(function* () {
const project = yield* Project.Service
@@ -202,12 +202,12 @@ export const layer: Layer.Layer<Service, never, Project.Service | InstanceBootst
}),
)
export const defaultLayer = layer.pipe(Layer.provide(Project.defaultLayer))
export const bootstrapNode = LayerNode.unbound(InstanceBootstrap.Service, Node.tags.values.global)
export const node = LayerNode.make({
export const node = makeGlobalNode({
service: Service,
layer: layer,
deps: [Project.node, InstanceBootstrapGraph.node],
deps: [Project.node, bootstrapNode],
})
export * as InstanceStore from "./instance-store"
+1 -12
View File
@@ -103,7 +103,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Pr
type GitResult = { code: number; text: string; stderr: string }
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@@ -463,17 +463,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(ProjectV2.defaultLayer),
Layer.provide(ProjectDirectories.defaultLayer),
Layer.provide(AppProcess.defaultLayer),
Layer.provide(CrossSpawnSpawner.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Database.defaultLayer),
Layer.provide(RuntimeFlags.defaultLayer),
)
export const use = serviceUse(Service)
export const node = LayerNode.make({
+1 -3
View File
@@ -295,7 +295,7 @@ interface State {
export class Service extends Context.Service<Service, Interface>()("@opencode/Vcs") {}
export const layer: Layer.Layer<Service, never, Git.Service | EventV2Bridge.Service> = Layer.effect(
const layer: Layer.Layer<Service, never, Git.Service | EventV2Bridge.Service> = Layer.effect(
Service,
Effect.gen(function* () {
const git = yield* Git.Service
@@ -418,8 +418,6 @@ export const layer: Layer.Layer<Service, never, Git.Service | EventV2Bridge.Serv
}),
)
export const defaultLayer = layer.pipe(Layer.provide(Git.defaultLayer), Layer.provide(EventV2Bridge.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [Git.node, EventV2Bridge.node] })
export * as Vcs from "./vcs"
+1 -5
View File
@@ -106,7 +106,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Pr
export const use = serviceUse(Service)
export const layer: Layer.Layer<Service, never, Auth.Service | Plugin.Service> = Layer.effect(
const layer: Layer.Layer<Service, never, Auth.Service | Plugin.Service> = Layer.effect(
Service,
Effect.gen(function* () {
const auth = yield* Auth.Service
@@ -224,10 +224,6 @@ export const layer: Layer.Layer<Service, never, Auth.Service | Plugin.Service> =
}),
)
export const defaultLayer = Layer.suspend(() =>
layer.pipe(Layer.provide(Auth.defaultLayer), Layer.provide(Plugin.defaultLayer)),
)
export const node = LayerNode.make({ service: Service, layer: layer, deps: [Auth.node, Plugin.node] })
export * as ProviderAuth from "./auth"
+1 -13
View File
@@ -1299,7 +1299,7 @@ function modelSuggestions(provider: Info | undefined, modelID: ModelV2.ID, enabl
.map((item) => item.id)
}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@@ -1949,18 +1949,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = Layer.suspend(() =>
layer.pipe(
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Env.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(Auth.defaultLayer),
Layer.provide(Plugin.defaultLayer),
Layer.provide(ModelsDev.defaultLayer),
Layer.provide(RuntimeFlags.defaultLayer),
),
)
const priority = ["gpt-5", "claude-sonnet-4", "big-pickle", "gemini-3-pro"]
const smallModelFamilyPriority = ["gemini-flash", "gpt-nano", "claude-haiku"]
export function sort<T extends { id: string }>(models: T[]) {
+25 -7
View File
@@ -463,7 +463,9 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re
if (
options.store !== true &&
key &&
["@ai-sdk/openai", "@ai-sdk/azure", "@ai-sdk/amazon-bedrock/mantle"].includes(model.api.npm)
["@ai-sdk/openai", "@ai-sdk/azure", "@ai-sdk/amazon-bedrock/mantle", "@ai-sdk/github-copilot"].includes(
model.api.npm,
)
) {
msgs = mapProviderOptions(msgs, (options) => {
if (!options?.[key] || !("itemId" in options[key])) return options
@@ -605,8 +607,14 @@ function anthropicOpus47OrLater(apiId: string) {
return major > 4 || (major === 4 && minor >= 7)
}
function anthropicSonnet5OrLater(apiId: string) {
const version = /sonnet-(\d+)(?:[.@-]|$)|claude-(\d+)-sonnet(?:[.@-]|$)/i.exec(apiId)
if (!version) return false
return Number(version[1] ?? version[2]) >= 5
}
function anthropicAdaptiveEfforts(apiId: string): string[] | null {
if (anthropicOpus47OrLater(apiId) || apiId.includes("fable-5")) {
if (anthropicOpus47OrLater(apiId) || anthropicSonnet5OrLater(apiId) || apiId.includes("fable-5")) {
return ["low", "medium", "high", "xhigh", "max"]
}
if (
@@ -620,7 +628,7 @@ function anthropicAdaptiveEfforts(apiId: string): string[] | null {
}
function anthropicOmitsThinking(apiId: string) {
return anthropicOpus47OrLater(apiId) || apiId.includes("fable-5")
return anthropicOpus47OrLater(apiId) || anthropicSonnet5OrLater(apiId) || apiId.includes("fable-5")
}
function googleThinkingLevelEfforts(apiId: string) {
@@ -1255,6 +1263,16 @@ const SLUG_OVERRIDES: Record<string, string> = {
}
export function providerOptions(model: Provider.Model, options: { [x: string]: any }) {
const usesOpenAIReasoningGate =
model.api.npm === "@ai-sdk/openai" ||
model.api.npm === "@ai-sdk/azure" ||
model.api.npm === "@ai-sdk/amazon-bedrock/mantle"
const normalized =
usesOpenAIReasoningGate &&
(model.capabilities.reasoning || options.reasoningEffort !== undefined || options.reasoningSummary !== undefined)
? { ...options, forceReasoning: true }
: options
if (model.api.npm === "@ai-sdk/gateway") {
// Gateway providerOptions are split across two namespaces:
// - `gateway`: gateway-native routing/caching controls (order, only, byok, etc.)
@@ -1264,8 +1282,8 @@ export function providerOptions(model: Provider.Model, options: { [x: string]: a
const i = model.api.id.indexOf("/")
const rawSlug = i > 0 ? model.api.id.slice(0, i) : undefined
const slug = rawSlug ? (SLUG_OVERRIDES[rawSlug] ?? rawSlug) : undefined
const gateway = options.gateway
const rest = Object.fromEntries(Object.entries(options).filter(([k]) => k !== "gateway"))
const gateway = normalized.gateway
const rest = Object.fromEntries(Object.entries(normalized).filter(([k]) => k !== "gateway"))
const has = Object.keys(rest).length > 0
const result: Record<string, any> = {}
@@ -1299,9 +1317,9 @@ export function providerOptions(model: Provider.Model, options: { [x: string]: a
// providerOptions["openai"], but OpenAIResponsesLanguageModel checks
// "azure" first. Pass both so model options work on either code path.
if (model.api.npm === "@ai-sdk/azure") {
return { openai: options, azure: options }
return { openai: normalized, azure: normalized }
}
return { [key]: options }
return { [key]: normalized }
}
export function maxOutputTokens(model: Provider.Model, outputTokenMax = OUTPUT_TOKEN_MAX): number {
+1 -3
View File
@@ -61,7 +61,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/Question") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2Bridge.Service
@@ -156,8 +156,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(EventV2Bridge.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [EventV2Bridge.node] })
export * as Question from "."
@@ -51,6 +51,7 @@ import { Worktree } from "@/worktree"
import { RuntimeFlags } from "@/effect/runtime-flags"
import { MoveSession } from "@opencode-ai/core/control-plane/move-session"
import { Database } from "@opencode-ai/core/database/database"
import { AppNodeBuilderV1 } from "@/effect/app-node-builder-v1"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { httpClient } from "@opencode-ai/core/effect/app-node-platform"
import { EventV2 } from "@opencode-ai/core/event"
@@ -63,6 +64,7 @@ import { PtyTicket } from "@opencode-ai/core/pty/ticket"
import { Ripgrep } from "@opencode-ai/core/ripgrep"
import { SessionProjector } from "@opencode-ai/core/session/projector"
import { SessionV2 } from "@opencode-ai/core/session"
import { SessionExecution } from "@opencode-ai/core/session/execution"
import * as SessionExecutionLocal from "@opencode-ai/core/session/execution/local"
import { lazy } from "@/util/lazy"
import { CorsConfig, isAllowedCorsOrigin, type CorsOptions } from "@opencode-ai/server/cors"
@@ -98,7 +100,7 @@ import { sessionHandlers } from "./handlers/session"
import { syncHandlers } from "./handlers/sync"
import { tuiHandlers } from "./handlers/tui"
import { handlers } from "@opencode-ai/server/handlers"
import { locationServiceMapLayer } from "@opencode-ai/core/location-services"
import { buildLocationServiceMap, LocationServiceMap } from "@opencode-ai/core/location-services"
import { layer as locationLayer } from "@opencode-ai/server/location"
import { sessionLocationLayer } from "@opencode-ai/server/middleware/session-location"
import { PtyEnvironment } from "@opencode-ai/server/pty-environment"
@@ -131,10 +133,10 @@ const cors = (corsOptions?: CorsOptions) =>
// - ptyConnectApiRoutes: typed WebSocket upgrade route with ticket-aware auth.
// - instanceApiRoutes: remaining typed instance routes.
// - uiRoute: raw catch-all fallback; auth is router middleware so public static assets can bypass it.
const authOnlyRouterLayer = authorizationRouterMiddleware.layer.pipe(Layer.provide(ServerAuth.Config.defaultLayer))
const httpApiAuthLayer = authorizationLayer.pipe(Layer.provide(ServerAuth.Config.defaultLayer))
const ptyConnectHttpApiAuthLayer = ptyConnectAuthorizationLayer.pipe(Layer.provide(ServerAuth.Config.defaultLayer))
const serverHttpApiAuthLayer = serverAuthorizationLayer.pipe(Layer.provide(ServerAuth.Config.defaultLayer))
const authOnlyRouterLayer = authorizationRouterMiddleware.layer.pipe(Layer.provide(ServerAuth.Config.layer))
const httpApiAuthLayer = authorizationLayer.pipe(Layer.provide(ServerAuth.Config.layer))
const ptyConnectHttpApiAuthLayer = ptyConnectAuthorizationLayer.pipe(Layer.provide(ServerAuth.Config.layer))
const serverHttpApiAuthLayer = serverAuthorizationLayer.pipe(Layer.provide(ServerAuth.Config.layer))
const workspaceRoutingLive = workspaceRoutingLayer.pipe(Layer.provide(Socket.layerWebSocketConstructorGlobal))
const rootApiRoutes = HttpApiBuilder.layer(RootHttpApi).pipe(
Layer.provide([controlHandlers, controlPlaneHandlers, globalHandlers]),
@@ -269,6 +271,8 @@ const app = LayerNode.group([
export function createRoutes(
corsOptions?: CorsOptions,
): Layer.Layer<never, EffectConfig.ConfigError, RouteRequirements> {
const locationServiceMapV2 = buildLocationServiceMap()
return Layer.mergeAll(
rootApiRoutes,
eventApiRoutes,
@@ -284,7 +288,7 @@ export function createRoutes(
corsVaryFix,
fenceLayer,
cors(corsOptions),
MoveSession.defaultLayer,
AppNodeBuilderV1.build(MoveSession.node, [[LocationServiceMap.node, locationServiceMapV2]]),
HttpServer.layerServices,
]),
Layer.provide(Layer.succeed(CorsConfig)(corsOptions)),
@@ -294,14 +298,14 @@ export function createRoutes(
Layer.provide(locationLayer),
Layer.provide(PtyEnvironment.layer),
Layer.provide(
SessionV2.defaultLayer.pipe(
Layer.provide(SessionExecutionLocal.defaultLayer),
Layer.provide(locationServiceMapLayer),
),
AppNodeBuilderV1.build(SessionV2.node, [
[LocationServiceMap.node, locationServiceMapV2],
[SessionExecution.node, SessionExecutionLocal.node],
]),
),
Layer.provide(locationServiceMapLayer),
Layer.provide(locationServiceMapV2),
Layer.provide(LayerNode.compile(app)),
Layer.provide(AppNodeBuilderV1.build(app)),
)
}
@@ -1,4 +1,5 @@
import { Context, Effect, Layer, Option } from "effect"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import * as Socket from "effect/unstable/socket/Socket"
export const SERVER_CLOSING_EVENT = () => new Socket.CloseEvent(1001, "server closing")
@@ -13,7 +14,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/HttpApiWebSocketTracker") {}
export const layer = Layer.sync(Service)(() => {
const layer = Layer.sync(Service)(() => {
const sockets = new Set<Close>()
let closing = false
return Service.of({
@@ -44,6 +45,8 @@ export const layer = Layer.sync(Service)(() => {
})
})
export const node = LayerNode.make({ service: Service, layer, deps: [] })
export const register = (close: Close) =>
Effect.gen(function* () {
const tracker = yield* Effect.serviceOption(Service)
+2 -1
View File
@@ -1,6 +1,7 @@
import "./init-projectors"
import { NodeHttpServer } from "@effect/platform-node"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { ConfigProvider, Context, Effect, Exit, Layer, Scope } from "effect"
import { HttpRouter, HttpServer } from "effect/unstable/http"
import { OpenApi } from "effect/unstable/httpapi"
@@ -102,7 +103,7 @@ function listenerLayer(opts: ListenOptions, port: number) {
disableLogger: true,
disableListenLog: true,
}).pipe(
Layer.provideMerge(WebSocketTracker.layer),
Layer.provideMerge(AppNodeBuilder.build(WebSocketTracker.node)),
Layer.provideMerge(serverLayer({ port, hostname: opts.hostname })),
// Install a fresh `ConfigProvider` per listener so `Config.string(...)`
// reads reflect the current `process.env`. Effect's default
+1 -14
View File
@@ -153,7 +153,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Se
export const use = serviceUse(Service)
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const config = yield* Config.Service
@@ -544,19 +544,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = Layer.suspend(() =>
layer.pipe(
Layer.provide(Provider.defaultLayer),
Layer.provide(Session.defaultLayer),
Layer.provide(SessionProcessor.defaultLayer),
Layer.provide(Agent.defaultLayer),
Layer.provide(Plugin.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(RuntimeFlags.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
),
)
export const node = LayerNode.make({
service: Service,
layer: layer,
+1 -9
View File
@@ -45,7 +45,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/Instruction") {}
export const layer: Layer.Layer<
const layer: Layer.Layer<
Service,
never,
FSUtil.Service | Config.Service | Global.Service | HttpClient.HttpClient | RuntimeFlags.Service
@@ -224,14 +224,6 @@ export const layer: Layer.Layer<
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Config.defaultLayer),
Layer.provide(Global.layer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(FetchHttpClient.layer),
Layer.provide(RuntimeFlags.defaultLayer),
)
export function loaded(messages: SessionV1.WithParts[]) {
return extract(messages)
}
+2 -17
View File
@@ -8,7 +8,7 @@ import { Context, Effect, Layer } from "effect"
import * as Stream from "effect/Stream"
import { streamText, wrapLanguageModel, type ModelMessage, type Tool } from "ai"
import type { LLMEvent } from "@opencode-ai/llm"
import { LLMClient, RequestExecutor, WebSocketExecutor } from "@opencode-ai/llm/route"
import { LLMClient } from "@opencode-ai/llm/route"
import type { LLMClientService } from "@opencode-ai/llm/route"
import { GitLabWorkflowLanguageModel } from "gitlab-ai-provider"
import { ProviderTransform } from "@/provider/transform"
@@ -384,26 +384,11 @@ const live: Layer.Layer<
}),
)
export const layer = live.pipe(Layer.provide(Permission.defaultLayer), Layer.provide(EventV2Bridge.defaultLayer))
export const defaultLayer = Layer.suspend(() =>
layer.pipe(
Layer.provide(Auth.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(Provider.defaultLayer),
Layer.provide(Plugin.defaultLayer),
Layer.provide(
LLMClient.layer.pipe(Layer.provide(Layer.mergeAll(RequestExecutor.defaultLayer, WebSocketExecutor.layer))),
),
Layer.provide(RuntimeFlags.defaultLayer),
),
)
export const hasToolCalls = LLMRequestPrep.hasToolCalls
export const node = LayerNode.make({
service: Service,
layer: layer,
layer: live,
deps: [
Auth.node,
Config.node,
+1 -18
View File
@@ -78,7 +78,7 @@ type StreamEvent = LLMEvent
export class Service extends Context.Service<Service, Interface>()("@opencode/SessionProcessor") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const session = yield* Session.Service
@@ -694,23 +694,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = Layer.suspend(() =>
layer.pipe(
Layer.provide(Session.defaultLayer),
Layer.provide(Snapshot.defaultLayer),
Layer.provide(Agent.defaultLayer),
Layer.provide(LLM.defaultLayer),
Layer.provide(Permission.defaultLayer),
Layer.provide(Plugin.defaultLayer),
Layer.provide(SessionSummary.defaultLayer),
Layer.provide(SessionStatus.defaultLayer),
Layer.provide(Image.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(Database.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
),
)
export const node = LayerNode.make({
service: Service,
layer: layer,
+1 -35
View File
@@ -110,7 +110,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/SessionPrompt") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const status = yield* SessionStatus.Service
@@ -1490,40 +1490,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = Layer.suspend(() =>
layer.pipe(
Layer.provide(SessionRunState.defaultLayer),
Layer.provide(SessionStatus.defaultLayer),
Layer.provide(SessionCompaction.defaultLayer),
Layer.provide(SessionProcessor.defaultLayer),
Layer.provide(Command.defaultLayer),
Layer.provide(Permission.defaultLayer),
Layer.provide(MCP.defaultLayer),
Layer.provide(LSP.defaultLayer),
Layer.provide(ToolRegistry.defaultLayer),
Layer.provide(Truncate.defaultLayer),
Layer.provide(Provider.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(Instruction.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Plugin.defaultLayer),
Layer.provide(Session.defaultLayer),
Layer.provide(SessionRevert.defaultLayer),
Layer.provide(SessionSummary.defaultLayer),
Layer.provide(Image.defaultLayer),
Layer.provide(
Layer.mergeAll(
Agent.defaultLayer,
Database.defaultLayer,
SystemPrompt.defaultLayer,
LLM.defaultLayer,
CrossSpawnSpawner.defaultLayer,
RuntimeFlags.defaultLayer,
EventV2Bridge.defaultLayer,
),
),
),
)
const ModelRef = Schema.Struct({
providerID: ProviderV2.ID,
modelID: ModelV2.ID,
+1 -12
View File
@@ -25,7 +25,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/SessionRevert") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const sessions = yield* Session.Service
@@ -137,17 +137,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = Layer.suspend(() =>
layer.pipe(
Layer.provide(SessionRunState.defaultLayer),
Layer.provide(Session.defaultLayer),
Layer.provide(Snapshot.defaultLayer),
Layer.provide(Storage.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(SessionSummary.defaultLayer),
),
)
export const node = LayerNode.make({
service: Service,
layer: layer,
+1 -3
View File
@@ -26,7 +26,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/SessionRunState") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const jobs = yield* Job.Service
@@ -108,8 +108,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(Job.defaultLayer), Layer.provide(SessionStatus.defaultLayer))
const cancelJobs = Effect.fn("SessionRunState.cancelJobs")(function* (jobs: Job.Interface, sessionID: SessionID) {
const running = yield* jobs.list()
const pending = new Set<string>([sessionID])
+1 -89
View File
@@ -9,11 +9,8 @@ import { Decimal } from "decimal.js"
import type { ProviderMetadata, Usage } from "@opencode-ai/llm"
import { InstallationVersion } from "@opencode-ai/core/installation/version"
import { Database } from "@opencode-ai/core/database/database"
import { makeRuntime } from "@opencode-ai/core/effect/runtime"
import { EventV2Bridge } from "@/event-v2-bridge"
import { SessionV2 } from "@opencode-ai/core/session"
import * as SessionExecutionLocal from "@opencode-ai/core/session/execution/local"
import { locationServiceMapLayer } from "@opencode-ai/core/location-services"
import { NotFoundError } from "@/storage/storage"
import { eq } from "drizzle-orm"
@@ -46,8 +43,6 @@ import { ProviderV2 } from "@opencode-ai/core/provider"
import { ModelV2 } from "@opencode-ai/core/model"
import { SessionMessage } from "@opencode-ai/schema/session-message"
const runtime = makeRuntime(Database.Service, Database.defaultLayer)
const parentTitlePrefix = "New session - "
const childTitlePrefix = "Child session - "
@@ -488,7 +483,7 @@ export type Patch = Omit<Partial<Info>, "time" | "share" | "summary" | "revert"
permission?: Info["permission"] | null
}
export const layer: Layer.Layer<
const layer: Layer.Layer<
Service,
never,
Job.Service | RuntimeFlags.Service | Database.Service | EventV2Bridge.Service
@@ -940,19 +935,6 @@ export const layer: Layer.Layer<
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Job.defaultLayer),
Layer.provide(Database.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(
SessionV2.defaultLayer.pipe(
Layer.provide(SessionExecutionLocal.defaultLayer),
Layer.provide(locationServiceMapLayer),
),
),
Layer.provide(RuntimeFlags.defaultLayer),
)
const cancelJobs = Effect.fn("Session.cancelJobs")(function* (jobs: Job.Interface, sessionID: SessionID) {
const running = yield* jobs.list()
yield* Effect.forEach(
@@ -1022,76 +1004,6 @@ function listByProject(
)
}
export function* listGlobal(input?: {
directory?: string
roots?: boolean
start?: number
cursor?: number
search?: string
limit?: number
archived?: boolean
}) {
const conditions: SQL[] = []
if (input?.directory) {
conditions.push(eq(SessionTable.directory, input.directory))
}
if (input?.roots) {
conditions.push(isNull(SessionTable.parent_id))
}
if (input?.start) {
conditions.push(gte(SessionTable.time_updated, input.start))
}
if (input?.cursor) {
conditions.push(lt(SessionTable.time_updated, input.cursor))
}
if (input?.search) {
conditions.push(like(SessionTable.title, `%${input.search}%`))
}
if (!input?.archived) {
conditions.push(isNull(SessionTable.time_archived))
}
const limit = input?.limit ?? 100
const rows = runtime.runSync(({ db }) => {
const query =
conditions.length > 0
? db
.select()
.from(SessionTable)
.where(and(...conditions))
: db.select().from(SessionTable)
return query.orderBy(desc(SessionTable.time_updated), desc(SessionTable.id)).limit(limit).all().pipe(Effect.orDie)
})
const ids = [...new Set(rows.map((row) => row.project_id))]
const projects = new Map<string, ProjectInfo>()
if (ids.length > 0) {
const items = runtime.runSync(({ db }) =>
db
.select({ id: ProjectTable.id, name: ProjectTable.name, worktree: ProjectTable.worktree })
.from(ProjectTable)
.where(inArray(ProjectTable.id, ids))
.all()
.pipe(Effect.orDie),
)
for (const item of items) {
projects.set(item.id, {
id: item.id,
name: item.name ?? undefined,
worktree: item.worktree,
})
}
}
for (const row of rows) {
const project = projects.get(row.project_id) ?? null
yield { ...fromRow(row), project }
}
}
export const node = LayerNode.make({
service: Service,
layer: layer,
+1 -3
View File
@@ -18,7 +18,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/SessionStatus") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2Bridge.Service
@@ -51,8 +51,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(EventV2Bridge.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [EventV2Bridge.node] })
export * as SessionStatus from "./status"
+1 -10
View File
@@ -71,7 +71,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/SessionSummary") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const sessions = yield* Session.Service
@@ -145,15 +145,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = Layer.suspend(() =>
layer.pipe(
Layer.provide(Session.defaultLayer),
Layer.provide(Snapshot.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(Config.defaultLayer),
),
)
export const DiffInput = Schema.Struct({
sessionID: SessionID,
messageID: Schema.optional(MessageID),
+1 -7
View File
@@ -47,7 +47,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/SystemPrompt") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const skill = yield* Skill.Service
@@ -128,12 +128,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Skill.defaultLayer),
Layer.provide(MCP.defaultLayer),
Layer.provide(locationServiceMapLayer),
)
const locationServiceMapNode = LayerNode.make({
service: LocationServiceMap.Service,
layer: locationServiceMapLayer,
+1 -3
View File
@@ -20,7 +20,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/SessionTodo") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2Bridge.Service
@@ -69,8 +69,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(EventV2Bridge.defaultLayer), Layer.provide(Database.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [EventV2Bridge.node, Database.node] })
export * as Todo from "./todo"
+1 -8
View File
@@ -14,7 +14,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/SessionShare") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const cfg = yield* Config.Service
@@ -49,13 +49,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(ShareNext.defaultLayer),
Layer.provide(Session.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(RuntimeFlags.defaultLayer),
)
export const node = LayerNode.make({
service: Service,
layer: layer,
+1 -11
View File
@@ -109,7 +109,7 @@ function key(item: Data) {
}
}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const account = yield* Account.Service
@@ -362,16 +362,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(Account.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(Database.defaultLayer),
Layer.provide(FetchHttpClient.layer),
Layer.provide(Provider.defaultLayer),
Layer.provide(Session.defaultLayer),
)
export const node = LayerNode.make({
service: Service,
layer: layer,
+1 -7
View File
@@ -26,7 +26,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/SkillDiscovery") {}
export const layer: Layer.Layer<Service, never, FSUtil.Service | Path.Path | HttpClient.HttpClient> = Layer.effect(
const layer: Layer.Layer<Service, never, FSUtil.Service | Path.Path | HttpClient.HttpClient> = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@@ -135,12 +135,6 @@ export const layer: Layer.Layer<Service, never, FSUtil.Service | Path.Path | Htt
}),
)
export const defaultLayer: Layer.Layer<Service> = layer.pipe(
Layer.provide(FetchHttpClient.layer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(NodePath.layer),
)
export const node = LayerNode.make({ service: Service, layer: layer, deps: [FSUtil.node, path, httpClient] })
export * as Discovery from "./discovery"
+1 -10
View File
@@ -247,7 +247,7 @@ const loadSkills = Effect.fnUntraced(function* (
export class Service extends Context.Service<Service, Interface>()("@opencode/Skill") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const discovery = yield* Discovery.Service
@@ -318,15 +318,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Discovery.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Global.layer),
Layer.provide(RuntimeFlags.defaultLayer),
)
export function fmt(list: Info[], opts: { verbose: boolean }) {
const described = list.filter((skill) => skill.description !== undefined)
if (described.length === 0) return "No skills are currently available."
+1 -7
View File
@@ -46,7 +46,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/Snapshot") {}
export const layer: Layer.Layer<Service, never, FSUtil.Service | AppProcess.Service | Config.Service> = Layer.effect(
const layer: Layer.Layer<Service, never, FSUtil.Service | AppProcess.Service | Config.Service> = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@@ -798,12 +798,6 @@ export const layer: Layer.Layer<Service, never, FSUtil.Service | AppProcess.Serv
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(AppProcess.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Config.defaultLayer),
)
export const node = LayerNode.make({
service: Service,
layer: layer,
+1 -3
View File
@@ -210,7 +210,7 @@ const MIGRATIONS: Migration[] = [
}),
]
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@@ -322,8 +322,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(FSUtil.defaultLayer), Layer.provide(Git.defaultLayer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [FSUtil.node, Git.node] })
export * as Storage from "./storage"
+3 -27
View File
@@ -33,7 +33,6 @@ import { Glob } from "@opencode-ai/core/util/glob"
import path from "path"
import { pathToFileURL } from "url"
import { Effect, Layer, Context } from "effect"
import { FetchHttpClient, HttpClient } from "effect/unstable/http"
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { Format } from "../format"
@@ -80,7 +79,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/ToolRegistry") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const config = yield* Config.Service
@@ -315,30 +314,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = Layer.suspend(() =>
layer
.pipe(
Layer.provide(Config.defaultLayer),
Layer.provide(Plugin.defaultLayer),
Layer.provide(Question.defaultLayer),
Layer.provide(Todo.defaultLayer),
Layer.provide(Skill.defaultLayer),
Layer.provide(Agent.defaultLayer),
Layer.provide(Session.defaultLayer),
Layer.provide(Job.defaultLayer),
Layer.provide(Provider.defaultLayer),
Layer.provide(LSP.defaultLayer),
Layer.provide(Instruction.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(FetchHttpClient.layer),
Layer.provide(Format.defaultLayer),
Layer.provide(CrossSpawnSpawner.defaultLayer),
Layer.provide(Truncate.defaultLayer),
)
.pipe(Layer.provide(Database.defaultLayer), Layer.provide(RuntimeFlags.defaultLayer)),
)
function isZodType(value: unknown): value is z.ZodType {
return typeof value === "object" && value !== null && "_zod" in value
}
@@ -417,7 +392,7 @@ function isJsonSchemaObject(value: unknown): value is Record<string, unknown> {
export const node = LayerNode.make({
service: Service,
layer: layer.pipe(Layer.provide(Ripgrep.defaultLayer)),
layer,
deps: [
Config.node,
Plugin.node,
@@ -438,6 +413,7 @@ export const node = LayerNode.make({
Truncate.node,
RuntimeFlags.node,
Database.node,
Ripgrep.node,
],
})
+1 -3
View File
@@ -46,7 +46,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/Truncate") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@@ -151,8 +151,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(FSUtil.defaultLayer), Layer.provide(NodePath.layer))
export const node = LayerNode.make({ service: Service, layer: layer, deps: [FSUtil.node] })
export * as Truncate from "./truncate"
+1 -14
View File
@@ -1,7 +1,6 @@
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { path } from "@opencode-ai/core/effect/app-node-platform"
import { Global } from "@opencode-ai/core/global"
import { InstanceLayer } from "@/project/instance-layer"
import { InstanceStore } from "@/project/instance-store"
import { Project } from "@/project/project"
import { Database } from "@opencode-ai/core/database/database"
@@ -14,7 +13,6 @@ import { GlobalBus } from "@/bus/global"
import { Git } from "@/git"
import { Effect, Layer, Path, Schema, Scope, Context } from "effect"
import { ChildProcess } from "effect/unstable/process"
import { NodePath } from "@effect/platform-node"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { AppProcess } from "@opencode-ai/core/process"
import { InstanceState } from "@/effect/instance-state"
@@ -131,7 +129,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Wo
type GitResult = { code: number; text: string; stderr: string }
export const layer: Layer.Layer<
const layer: Layer.Layer<
Service,
never,
| FSUtil.Service
@@ -616,17 +614,6 @@ export const layer: Layer.Layer<
}),
)
export const appLayer = layer.pipe(
Layer.provide(Git.defaultLayer),
Layer.provide(AppProcess.defaultLayer),
Layer.provide(Project.defaultLayer),
Layer.provide(Database.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(NodePath.layer),
)
export const defaultLayer = appLayer.pipe(Layer.provide(InstanceLayer.layer))
export const node = LayerNode.make({
service: Service,
layer: layer,