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
commit 8c94e9005f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
590 changed files with 15772 additions and 5530 deletions

View file

@ -45,7 +45,7 @@ export interface Interface extends State.Transformable<Draft> {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Agent") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2.Service
@ -111,6 +111,4 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer
export const node = makeLocationNode({ service: Service, layer, deps: [EventV2.node] })

View file

@ -146,7 +146,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/AISDK") {}
export const locationLayer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
let sdkHooks: ((event: SDKEvent) => Effect.Effect<void> | void)[] = []
@ -232,6 +232,4 @@ export const locationLayer = Layer.effect(
}),
)
export const node = makeLocationNode({ service: Service, layer: locationLayer, deps: [] })
export const defaultLayer = locationLayer
export const node = makeLocationNode({ service: Service, layer, deps: [] })

View file

@ -61,7 +61,7 @@ export interface Interface extends State.Transformable<Draft> {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Catalog") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2.Service
@ -293,9 +293,4 @@ export const layer = Layer.effect(
const SMALL_MODEL_RE = /\b(nano|flash|lite|mini|haiku|small|fast)\b/
export const locationLayer = layer.pipe(
Layer.provideMerge(Integration.locationLayer),
Layer.provideMerge(Policy.locationLayer),
)
export const node = makeLocationNode({ service: Service, layer, deps: [EventV2.node, Policy.node, Integration.node] })

View file

@ -26,7 +26,7 @@ export interface Interface extends State.Transformable<Draft> {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Command") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.sync(() => {
const state = State.create<Data, Draft>({
@ -59,6 +59,4 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer
export const node = makeLocationNode({ service: Service, layer, deps: [] })

View file

@ -132,7 +132,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Config") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@ -218,8 +218,6 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer.pipe(Layer.provideMerge(Policy.locationLayer))
export const node = makeLocationNode({
service: Service,
layer,

View file

@ -1,6 +1,7 @@
export * as MoveSession from "./move-session"
import { Context, DateTime, Effect, Layer, Schema } from "effect"
import { makeGlobalNode } from "../effect/app-node"
import { EventV2 } from "../event"
import { Git } from "../git"
import { Location } from "../location"
@ -65,7 +66,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/ControlPlaneMoveSession") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const git = yield* Git.Service
@ -140,9 +141,8 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Git.defaultLayer),
Layer.provide(EventV2.defaultLayer),
Layer.provide(ProjectV2.defaultLayer),
Layer.provide(SessionStore.defaultLayer),
)
export const node = makeGlobalNode({
service: Service,
layer,
deps: [Git.node, EventV2.node, ProjectV2.node, SessionStore.node],
})

View file

@ -48,7 +48,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Credential") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const { db } = yield* Database.Service
@ -135,6 +135,4 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(Database.defaultLayer))
export const node = makeGlobalNode({ service: Service, layer, deps: [Database.node] })

View file

@ -497,12 +497,11 @@ export const make = Effect.gen(function* () {
return makeSpawner(spawnCommand)
})
export const layer: Layer.Layer<ChildProcessSpawner, never, FileSystem.FileSystem | Path.Path> = Layer.effect(
const layer: Layer.Layer<ChildProcessSpawner, never, FileSystem.FileSystem | Path.Path> = Layer.effect(
ChildProcessSpawner,
make,
)
export const defaultLayer = layer.pipe(Layer.provide(NodeFileSystem.layer), Layer.provide(NodePath.layer))
export const node = makeGlobalNode({ service: ChildProcessSpawner, layer, deps: [filesystem, path] })
export * as CrossSpawnSpawner from "./cross-spawn-spawner"

View file

@ -19,7 +19,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/storage/Database") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const db = yield* makeDatabase
@ -54,10 +54,4 @@ export function path() {
return join(Global.Path.data, `opencode-${InstallationChannel.replace(/[^a-zA-Z0-9._-]/g, "-")}.db`)
}
export const defaultLayer = Layer.unwrap(
Effect.gen(function* () {
return layerFromPath(path())
}),
).pipe(Layer.provide(Global.defaultLayer))
export const node = makeGlobalNode({ service: Service, layer: layerFromPath(path()), deps: [] })

View file

@ -1,24 +1,23 @@
import { Layer } from "effect"
import { buildLocationServiceMap } from "../location-services"
import { LocationServiceMap } from "../location-service-map"
import { LayerNode } from "./layer-node"
import { makeGlobalNode } from "./app-node"
export function build<A, E>(root: LayerNode.Node<A, E, any>, replacements?: readonly LayerNode.Replacement[]) {
const replacementMap = new Map(replacements?.map((item) => [item.source, item.replacement]))
export function build<A, E>(root: LayerNode.Node<A, E, any>, replacements: LayerNode.Replacements = []) {
let allReplacements = replacements
if (!LayerNode.hasUnbound(root, LocationServiceMap.node)) {
// If the location service map is not needed, we shouldn't pull it
// in. Compile the graph normally
return LayerNode.compile(root, replacementMap)
// Only build the location service map if it's actually needed
if (LayerNode.hasUnbound(root, LocationServiceMap.node) && !hasReplacement(replacements, LocationServiceMap.node)) {
const locationMap = buildLocationServiceMap(replacements)
const locationMapNode = makeGlobalNode({ service: LocationServiceMap.Service, layer: locationMap, deps: [] })
allReplacements = replacements.concat([[LocationServiceMap.node, locationMapNode]])
}
const locationMap = buildLocationServiceMap(replacementMap)
const locationMapNode = makeGlobalNode({ service: LocationServiceMap.Service, layer: locationMap, deps: [] })
return LayerNode.compile(root, allReplacements)
}
const app = LayerNode.bind(root, LocationServiceMap.node, locationMapNode)
return LayerNode.compile(app, replacementMap)
function hasReplacement(replacements: LayerNode.Replacements, node: LayerNode.Node<unknown, unknown, any>) {
return replacements.some(([source]) => source.name === node.name)
}
export * as AppNodeBuilder from "./app-node-builder"

View file

@ -0,0 +1 @@
File to save in: ~/.local/share/opencode/worktree/012780/location-layer-tiers/packages/core/src/effect/

View file

@ -111,20 +111,52 @@ export function group<const Items extends readonly AnyNode[]>(
return { kind: "group", name: "group", dependencies }
}
export type Replacement = {
readonly source: Layer.Any
readonly replacement: Layer.Any
}
export type Replacement = readonly [source: AnyNode, replacement: AnyNode | Layer.Any]
export type Replacements = readonly Replacement[]
type CheckReplacementErrors<SourceError, ReplacementError> = [Exclude<ReplacementError, SourceError>] extends [never]
? unknown
: { readonly "New replacement errors": Exclude<ReplacementError, SourceError> }
export function replace<A, E, R, E2>(
source: Layer.Layer<A, E, R>,
replacement: Layer.Layer<NoInfer<A>, E2, NoInfer<R>> & CheckReplacementErrors<E, NoInfer<E2>>,
): Replacement {
return { source, replacement }
type CheckReplacement<Item> = Item extends readonly [Node<infer A, infer E, infer T>, infer Replacement]
? Replacement extends Node<NoInfer<A>, infer E2, T>
? CheckReplacementErrors<E, NoInfer<E2>>
: Replacement extends Layer.Layer<NoInfer<A>, infer E2, never>
? CheckReplacementErrors<E, NoInfer<E2>>
: { readonly "Invalid replacement": Replacement }
: { readonly "Invalid replacement": Item }
type CheckReplacements<Items extends Replacements> = {
readonly [K in keyof Items]: CheckReplacement<Items[K]>
}
type ValidReplacements<Items extends Replacements> = Items & CheckReplacements<Items>
function replacementNode(source: AnyNode, replacement: AnyNode | Layer.Any) {
const replacementNode = isNode(replacement)
? replacement
: make({
...nodeMakeIdentity(source),
layer: replacement as Layer.Layer<unknown, unknown>,
deps: [],
tag: source.tag,
})
if (source.name !== replacementNode.name) {
throw new Error(`Cannot replace ${source.name} with ${replacementNode.name}`)
}
if (source.tag !== replacementNode.tag) {
throw new Error(`Cannot replace ${source.name} across tags`)
}
return replacementNode
}
function nodeMakeIdentity(node: AnyNode): NodeIdentity {
if (node.service !== undefined) return { service: node.service }
return { name: node.name }
}
function isNode(input: Layer.Any | AnyNode): input is AnyNode {
return "kind" in input && "dependencies" in input
}
// Tree -----------------------------------------------------------------------
@ -176,32 +208,38 @@ function walk<Result>(
return recur(root)
}
export function hoist<A, E, T extends Tag>(
export function hoist<A, E, T extends Tag, const Items extends Replacements = readonly []>(
root: Node<A, E, any>,
tag: T,
replacements?: ValidReplacements<Items>,
): {
readonly node: Node<A, E>
readonly hoisted: Node<unknown, E>
} {
const hoisted = new Map<string, AnyNode>()
const replacementMap = replacementMapFrom(replacements)
const node = walk<AnyNode>(root, (node, context) => {
if (node.kind === "group") {
return { ...node, dependencies: node.dependencies.map(context.visit) }
}
if (node.tag === tag) {
const existing = hoisted.get(node.name)
if (existing && existing !== node) {
throw new Error(`Tag ${tag} has conflicting implementations for ${node.name}`)
const node = walk<AnyNode>(
root,
(node, context) => {
if (node.kind === "group") {
return { ...node, dependencies: node.dependencies.map(context.visit) }
}
hoisted.set(node.name, node)
return group([])
}
if (node.kind === "unbound") {
return node
}
return { ...node, dependencies: node.dependencies.map(context.visit) }
})
if (node.tag === tag) {
const existing = hoisted.get(node.name)
if (existing && existing !== node) {
throw new Error(`Tag ${tag} has conflicting implementations for ${node.name}`)
}
hoisted.set(node.name, node)
return group([])
}
if (node.kind === "unbound") {
return node
}
return { ...node, dependencies: node.dependencies.map(context.visit) }
},
{ resolve: (node) => replacementMap.get(node.name) ?? node },
)
return {
node: node as Node<A, E>,
@ -209,10 +247,11 @@ export function hoist<A, E, T extends Tag>(
}
}
export function compile<A, E>(
export function compile<A, E, const Items extends Replacements = readonly []>(
root: Node<A, E, any>,
replacements?: ReadonlyMap<Layer.Any, Layer.Any>,
replacements?: ValidReplacements<Items>,
): Layer.Layer<A, E> {
const replacementMap = replacementMapFrom(replacements)
const cache = new Map<AnyNode, RuntimeLayer>()
const compileNode = (node: AnyNode) =>
walk<RuntimeLayer>(
@ -220,18 +259,65 @@ export function compile<A, E>(
(node, context) => {
if (node.kind === "unbound") throw new Error(`Unbound layer node: ${node.name}`)
const dependencies = node.dependencies.flatMap(flatten).map(context.visit)
const implementation = (replacements?.get(node.implementation!) ?? node.implementation!) as RuntimeLayer
const implementation = node.implementation! as RuntimeLayer
return dependencies.length === 0
? implementation
: implementation.pipe(Layer.provide(dependencies as [RuntimeLayer, ...RuntimeLayer[]]))
},
{ cache },
{ cache, resolve: (node) => replacementMap.get(node.name) ?? node },
)
const layers = flatten(root).map((node) => compileNode(node))
const layer = layers.reduce<RuntimeLayer>((result, layer) => layer.pipe(Layer.provideMerge(result)), Layer.empty)
return layer as Layer.Layer<A, E>
}
function replacementMapFrom(replacements?: Replacements) {
return (
replacements?.reduce((map, [source, replacement]) => {
const normalized = rewriteReplacementDependencies(replacementNode(source, replacement), map)
const current = new Map([[source.name, normalized]])
for (const [name, node] of map) map.set(name, rewriteReplacementDependencies(node, current))
map.set(source.name, normalized)
return map
}, new Map<string, AnyNode>()) ?? new Map<string, AnyNode>()
)
}
function rewriteReplacementDependencies(root: AnyNode, replacements: ReadonlyMap<string, AnyNode>) {
if (replacements.size === 0) return root
const cache = new Map<AnyNode, AnyNode>()
const visiting = new Set<AnyNode>()
const stack: AnyNode[] = []
const recur = (node: AnyNode, isRoot = false): AnyNode => {
const target = isRoot ? node : (replacements.get(node.name) ?? node)
const cached = cache.get(target)
if (cached !== undefined || cache.has(target)) return cached!
if (visiting.has(target)) {
const start = stack.indexOf(target)
throw new Error(
`Cycle detected in layer tree: ${[...stack.slice(start), target].map((item) => item.name).join(" -> ")}`,
)
}
visiting.add(target)
stack.push(target)
try {
const dependencies = target.dependencies.map((dependency) => recur(dependency))
const result = dependencies.every((dependency, index) => dependency === target.dependencies[index])
? target
: { ...target, dependencies }
cache.set(target, result)
return result
} finally {
stack.pop()
visiting.delete(target)
}
}
return recur(root, true)
}
export function hasUnbound(root: Node<unknown, unknown, any>, source: AnyNode): boolean {
if (source.kind !== "unbound") throw new Error(`Cannot check non-unbound layer node: ${source.name}`)
return walk<boolean>(root, (node, context) => {
@ -240,32 +326,6 @@ export function hasUnbound(root: Node<unknown, unknown, any>, source: AnyNode):
})
}
export function bind<A, E, T extends Tag | undefined>(
root: Node<A, E, T>,
source: AnyNode,
replacement: AnyNode,
): Node<A, E, T> {
if (source.kind !== "unbound") throw new Error(`Cannot bind non-unbound layer node: ${source.name}`)
if (source.name !== replacement.name) {
throw new Error(`Cannot bind ${source.name} to ${replacement.name}`)
}
if (source.tag !== replacement.tag) {
throw new Error(`Cannot bind ${source.name} across tags`)
}
return walk<AnyNode>(
root,
(target, context) => {
if (target.kind === "unbound") return target
const dependencies: AnyNode[] = []
const clone = { ...target, dependencies }
context.cache.set(target, clone)
dependencies.push(...target.dependencies.map(context.visit))
return clone
},
{ detectCycles: false, resolve: (node) => (node === source ? replacement : node) },
) as Node<A, E, T>
}
function flatten(node: AnyNode): readonly AnyNode[] {
return node.kind === "group" ? node.dependencies.flatMap(flatten) : [node]
}

View file

@ -652,5 +652,3 @@ export const layerWith = (options?: LayerOptions) =>
export const layer = layerWith()
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [Database.node] })
export const defaultLayer = layer.pipe(Layer.provide(Database.defaultLayer))

View file

@ -71,7 +71,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/v2
* write under the same process-local lock so cooperating OpenCode mutations do
* not overwrite changes made from the same stale content.
*/
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@ -191,8 +191,6 @@ function sameBytes(left: Uint8Array, right: Uint8Array) {
return left.every((byte, index) => byte === right[index])
}
export const locationLayer = layer
export const node = makeLocationNode({ service: Service, layer, deps: [FSUtil.node] })
/**

View file

@ -111,10 +111,6 @@ const baseLayer = Layer.effect(
}),
)
export const layer = baseLayer.pipe(Layer.provide(FileSystemSearch.locationLayer), Layer.provide(FSUtil.defaultLayer))
export const locationLayer = layer
export const node = makeLocationNode({
service: Service,
layer: baseLayer,

View file

@ -232,6 +232,4 @@ export const fffLayer = Layer.effect(
const layer = Layer.unwrap(Effect.sync(() => (Flag.OPENCODE_DISABLE_FFF || !Fff.available() ? ripgrepLayer : fffLayer)))
export const locationLayer = layer
export const node = makeLocationNode({ service: Service, layer, deps: [FSUtil.node, Location.node, Ripgrep.node] })

View file

@ -55,7 +55,7 @@ export interface Interface {}
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/FileWatcher") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
if (Flag.OPENCODE_DISABLE_FILEWATCHER) return Service.of({})
@ -141,8 +141,6 @@ export const layer = Layer.effect(
),
)
export const locationLayer = layer.pipe(Layer.provide(Config.locationLayer), Layer.provide(Git.defaultLayer))
export const node = makeLocationNode({
service: Service,
layer,

View file

@ -49,7 +49,7 @@ export namespace FSUtil {
export const use = serviceUse(Service)
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem
@ -200,7 +200,6 @@ export namespace FSUtil {
}),
)
export const defaultLayer = layer.pipe(Layer.provide(NodeFileSystem.layer))
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [filesystem] })
// Pure helpers that don't need Effect (path manipulation, sync operations)

View file

@ -172,7 +172,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/GitV2") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@ -943,7 +943,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(FSUtil.defaultLayer), Layer.provide(AppProcess.defaultLayer))
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [FSUtil.node, AppProcess.node] })
interface Result {

View file

@ -86,7 +86,7 @@ export async function convertToOpenAIResponsesInput({
: {
image_url: `data:${mediaType};base64,${convertToBase64(part.data)}`,
}),
detail: part.providerOptions?.openai?.imageDetail,
detail: part.providerOptions?.copilot?.imageDetail,
}
} else if (part.mediaType === "application/pdf") {
if (part.data instanceof URL) {
@ -127,7 +127,7 @@ export async function convertToOpenAIResponsesInput({
input.push({
role: "assistant",
content: [{ type: "output_text", text: part.text }],
id: (part.providerOptions?.openai?.itemId as string) ?? undefined,
id: (part.providerOptions?.copilot?.itemId as string) ?? undefined,
})
break
}
@ -143,7 +143,7 @@ export async function convertToOpenAIResponsesInput({
input.push({
type: "local_shell_call",
call_id: part.toolCallId,
id: (part.providerOptions?.openai?.itemId as string) ?? undefined,
id: (part.providerOptions?.copilot?.itemId as string) ?? undefined,
action: {
type: "exec",
command: parsedInput.action.command,
@ -162,7 +162,7 @@ export async function convertToOpenAIResponsesInput({
call_id: part.toolCallId,
name: part.toolName,
arguments: JSON.stringify(part.input),
id: (part.providerOptions?.openai?.itemId as string) ?? undefined,
id: (part.providerOptions?.copilot?.itemId as string) ?? undefined,
})
break
}
@ -275,7 +275,7 @@ export async function convertToOpenAIResponsesInput({
const output = part.output
if (output.type === "execution-denied") {
const approvalId = (output.providerOptions?.openai as { approvalId?: string } | undefined)?.approvalId
const approvalId = (output.providerOptions?.copilot as { approvalId?: string } | undefined)?.approvalId
if (approvalId) {
continue

View file

@ -525,7 +525,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
type: "reasoning" as const,
text: summary.text,
providerMetadata: {
openai: {
copilot: {
itemId: part.id,
reasoningEncryptedContent: part.encrypted_content ?? null,
},
@ -563,7 +563,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
toolName: "local_shell",
input: JSON.stringify({ action: part.action } satisfies z.infer<typeof localShellInputSchema>),
providerMetadata: {
openai: {
copilot: {
itemId: part.id,
},
},
@ -574,7 +574,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
case "message": {
for (const contentPart of part.content) {
if (options.providerOptions?.openai?.logprobs && contentPart.logprobs) {
if (options.providerOptions?.copilot?.logprobs && contentPart.logprobs) {
logprobs.push(contentPart.logprobs)
}
@ -582,7 +582,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
type: "text",
text: contentPart.text,
providerMetadata: {
openai: {
copilot: {
itemId: part.id,
},
},
@ -622,7 +622,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
toolName: part.name,
input: part.arguments,
providerMetadata: {
openai: {
copilot: {
itemId: part.id,
},
},
@ -724,15 +724,15 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
}
const providerMetadata: SharedV3ProviderMetadata = {
openai: { responseId: response.id },
copilot: { responseId: response.id },
}
if (logprobs.length > 0) {
providerMetadata.openai.logprobs = logprobs
providerMetadata.copilot.logprobs = logprobs
}
if (typeof response.service_tier === "string") {
providerMetadata.openai.serviceTier = response.service_tier
providerMetadata.copilot.serviceTier = response.service_tier
}
return {
@ -954,7 +954,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
type: "text-start",
id: value.item.id,
providerMetadata: {
openai: {
copilot: {
itemId: value.item.id,
},
},
@ -971,7 +971,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
type: "reasoning-start",
id: `${value.item.id}:0`,
providerMetadata: {
openai: {
copilot: {
itemId: value.item.id,
reasoningEncryptedContent: value.item.encrypted_content ?? null,
},
@ -994,7 +994,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
toolName: value.item.name,
input: value.item.arguments,
providerMetadata: {
openai: {
copilot: {
itemId: value.item.id,
},
},
@ -1103,7 +1103,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
},
} satisfies z.infer<typeof localShellInputSchema>),
providerMetadata: {
openai: { itemId: value.item.id },
copilot: { itemId: value.item.id },
},
})
} else if (value.item.type === "message") {
@ -1122,7 +1122,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
type: "reasoning-end",
id: `${activeReasoningPart.canonicalId}:${summaryIndex}`,
providerMetadata: {
openai: {
copilot: {
itemId: activeReasoningPart.canonicalId,
reasoningEncryptedContent: value.item.encrypted_content ?? null,
},
@ -1209,7 +1209,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
type: "text-start",
id: currentTextId,
providerMetadata: {
openai: { itemId: value.item_id },
copilot: { itemId: value.item_id },
},
})
}
@ -1220,7 +1220,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
delta: value.delta,
})
if (options.providerOptions?.openai?.logprobs && value.logprobs) {
if (options.providerOptions?.copilot?.logprobs && value.logprobs) {
logprobs.push(value.logprobs)
}
} else if (isResponseReasoningSummaryPartAddedChunk(value)) {
@ -1235,7 +1235,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
type: "reasoning-start",
id: `${activeItem.canonicalId}:${value.summary_index}`,
providerMetadata: {
openai: {
copilot: {
itemId: activeItem.canonicalId,
reasoningEncryptedContent: activeItem.encryptedContent ?? null,
},
@ -1252,7 +1252,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
id: `${activeItem.canonicalId}:${value.summary_index}`,
delta: value.delta,
providerMetadata: {
openai: {
copilot: {
itemId: activeItem.canonicalId,
},
},
@ -1306,17 +1306,17 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
}
const providerMetadata: SharedV3ProviderMetadata = {
openai: {
copilot: {
responseId,
},
}
if (logprobs.length > 0) {
providerMetadata.openai.logprobs = logprobs
providerMetadata.copilot.logprobs = logprobs
}
if (serviceTier !== undefined) {
providerMetadata.openai.serviceTier = serviceTier
providerMetadata.copilot.serviceTier = serviceTier
}
controller.enqueue({

View file

@ -71,12 +71,11 @@ export function make(input: Partial<Interface> = {}): Interface {
}
}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.sync(() => Service.of(make())),
)
export const defaultLayer = layer
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [] })
export const layerWith = (input: Partial<Interface>) =>

View file

@ -44,7 +44,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
@ -76,6 +76,4 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer.pipe(Layer.provide(Config.locationLayer))
export const node = makeLocationNode({ service: Service, layer, deps: [Config.node] })

View file

@ -19,7 +19,7 @@ class File extends Schema.Class<File>("InstructionContext.File")({
const Files = Schema.Array(File)
const key = SystemContext.Key.make("core/instructions")
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const fs = yield* FSUtil.Service
const global = yield* Global.Service

View file

@ -218,7 +218,7 @@ type TerminalAttempt = {
}
type AttemptEntry = PendingAttempt | TerminalAttempt
export const locationLayer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const credentials = yield* Credential.Service
@ -517,4 +517,4 @@ export const locationLayer = Layer.effect(
}),
)
export const node = makeLocationNode({ service: Service, layer: locationLayer, deps: [Credential.node, EventV2.node] })
export const node = makeLocationNode({ service: Service, layer, deps: [Credential.node, EventV2.node] })

View file

@ -363,8 +363,6 @@ export const make = Effect.gen(function* () {
return Service.of({ list, get, start, wait, block, background, backgroundAll, cancel })
})
export const layer = Layer.effect(Service, make)
export const defaultLayer = layer
const layer = Layer.effect(Service, make)
export const node = makeGlobalNode({ service: Service, layer, deps: [] })

View file

@ -78,7 +78,7 @@ interface ResolvedPath {
const slash = (value: string) => value.replaceAll("\\", "/")
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@ -160,8 +160,6 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer
export const node = makeLocationNode({
service: Service,
layer: layer.pipe(Layer.orDie),

View file

@ -96,17 +96,16 @@ export type LocationServices = LayerNode.Output<typeof locationServices>
export type LocationError = LayerNode.Error<typeof locationServices>
export function buildLocationServiceMap(
replacements?: ReadonlyMap<Layer.Any, Layer.Any>,
replacements: LayerNode.Replacements = [],
): Layer.Layer<LocationServiceMap.Service> {
return Layer.effect(
LocationServiceMap.Service,
LayerMap.make(
(ref: Location.Ref) => {
const location = LayerNode.hoist(
LayerNode.bind(locationServices, Location.node, Location.boundNode(ref)),
Node.tags.values.global,
)
return LayerNode.compile(location.node, replacements).pipe(
const allReplacements = replacements.concat([[Location.node, Location.boundNode(ref)]])
const location = LayerNode.hoist(locationServices, Node.tags.values.global, allReplacements)
return LayerNode.compile(location.node).pipe(
Layer.fresh,
Layer.tap(() =>
Effect.logInfo("booting location services", {
@ -114,7 +113,7 @@ export function buildLocationServiceMap(
workspaceID: ref.workspaceID,
}),
),
Layer.provide(LayerNode.compile(location.hoisted, replacements)),
Layer.provide(LayerNode.compile(location.hoisted)),
)
},
{ idleTimeToLive: "60 minutes" },

View file

@ -16,7 +16,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/Lo
export const node = LayerNode.unbound(Service, tags.values.location)
export const layer = (ref: Ref) =>
const layer = (ref: Ref) =>
Layer.effect(
Service,
Effect.gen(function* () {

View file

@ -73,6 +73,4 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer
export const node = makeLocationNode({ service: Service, layer, deps: [MCP.node] })

View file

@ -1,6 +1,6 @@
import path from "path"
import { Context, Duration, Effect, Layer, Option, Schedule, Schema } from "effect"
import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http"
import { HttpClient, HttpClientRequest } from "effect/unstable/http"
import { ModelsDev } from "@opencode-ai/schema/models-dev"
import { Global } from "./global"
import { Flag } from "./flag/flag"
@ -136,7 +136,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/ModelsDev") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@ -255,11 +255,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(FetchHttpClient.layer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(EventV2.defaultLayer),
)
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [FSUtil.node, EventV2.node, httpClient] })
export * as ModelsDev from "./models-dev"

View file

@ -9,6 +9,7 @@ import { Global } from "./global"
import { EffectFlock } from "./util/effect-flock"
import { makeGlobalNode } from "./effect/app-node"
import { filesystem } from "./effect/app-node-platform"
import { LayerNode } from "./effect/layer-node"
import { makeRuntime } from "./effect/runtime"
import { NpmConfig } from "./npm-config"
@ -68,7 +69,7 @@ interface ArboristTree {
edgesOut: Map<string, { to?: ArboristNode }>
}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const afs = yield* FSUtil.Service
@ -247,19 +248,13 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(EffectFlock.layer),
Layer.provide(FSUtil.layer),
Layer.provide(Global.layer),
Layer.provide(NodeFileSystem.layer),
)
export const node = makeGlobalNode({
service: Service,
layer: layer,
deps: [FSUtil.node, Global.node, filesystem, EffectFlock.node],
})
const { runPromise } = makeRuntime(Service, defaultLayer)
const { runPromise } = makeRuntime(Service, LayerNode.compile(node))
export async function install(...args: Parameters<Interface["install"]>) {
return runPromise((svc) => svc.install(...args))

View file

@ -1,6 +1,7 @@
export * as Observability from "./observability"
import { NodeFileSystem } from "@effect/platform-node"
import { LayerNode } from "./effect/layer-node"
import { Effect, Layer, Logger, References } from "effect"
import { FetchHttpClient } from "effect/unstable/http"
import { OtlpSerialization } from "effect/unstable/observability"
@ -19,3 +20,5 @@ export const layer = Layer.unwrap(
return Layer.merge(logs, yield* Effect.promise(Otlp.tracingLayer))
}),
)
export const node = LayerNode.make({ name: "observability", layer, deps: [] })

View file

@ -106,7 +106,7 @@ interface Pending {
readonly deferred: Deferred.Deferred<void, RejectedError | CorrectedError>
}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
EffectRuntime.gen(function* () {
const events = yield* EventV2.Service
@ -300,8 +300,6 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer.pipe(Layer.provideMerge(AgentV2.locationLayer))
export const node = makeLocationNode({
service: Service,
layer,

View file

@ -34,7 +34,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/PermissionSaved") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const { db } = yield* Database.Service
@ -76,6 +76,4 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(Database.defaultLayer))
export const node = makeGlobalNode({ service: Service, layer, deps: [Database.node] })

View file

@ -34,7 +34,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Plugin") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2.Service
@ -151,18 +151,6 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer.pipe(
Layer.provideMerge(AgentV2.locationLayer),
Layer.provideMerge(AISDK.locationLayer),
Layer.provideMerge(Catalog.locationLayer),
Layer.provideMerge(CommandV2.locationLayer),
Layer.provideMerge(Integration.locationLayer),
Layer.provideMerge(Reference.locationLayer),
Layer.provideMerge(SkillV2.locationLayer),
Layer.provideMerge(ToolRegistry.defaultLayer),
Layer.provideMerge(PluginRuntime.layer),
)
export const node = makeLocationNode({
service: Service,
layer,

View file

@ -32,7 +32,7 @@ import { SkillV2 } from "../skill"
import { State } from "../state"
import { ToolRegistry } from "../tool/registry"
import { Tools } from "../tool/tools"
import { FetchHttpClient, HttpClient } from "effect/unstable/http"
import { HttpClient } from "effect/unstable/http"
import { AgentPlugin } from "./agent"
import { CommandPlugin } from "./command"
import { ModelsDevPlugin } from "./models-dev"
@ -153,13 +153,6 @@ const layer = Layer.effectDiscard(
}),
)
export const locationLayer = layer.pipe(
Layer.provideMerge(PluginV2.locationLayer),
Layer.provideMerge(Config.locationLayer),
Layer.provideMerge(FileSystem.locationLayer),
Layer.provideMerge(FetchHttpClient.layer),
)
export const node = makeLocationNode({
name: "plugin-internal",
layer,

View file

@ -75,7 +75,7 @@ export const providerLayerWithCell = (cell: Cell) =>
const sessions = yield* SessionV2.Service
const jobs = yield* Job.Service
const locations = yield* LocationServiceMap.Service
const runtime = {
const runtime: Interface = {
session: sessions,
job: jobs,
location: {
@ -92,10 +92,10 @@ export const providerLayerWithCell = (cell: Cell) =>
}),
data: yield* agents.list(),
}
}).pipe(Effect.provide(locations.get(ref))),
}).pipe(Effect.provide(locations.get(ref)), Effect.orDie),
},
},
} satisfies Interface
}
cell.runtime = runtime
yield* Effect.addFinalizer(() =>
Effect.sync(() => {

View file

@ -22,7 +22,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Policy") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
EffectRuntime.gen(function* () {
let statements: Info[] = []
@ -44,6 +44,4 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer
export const node = makeLocationNode({ service: Service, layer, deps: [Location.node] })

View file

@ -136,7 +136,7 @@ export const collectStream = (stream: Stream.Stream<Uint8Array, PlatformError>,
},
).pipe(Effect.map((x) => ({ buffer: Buffer.concat(x.chunks), truncated: x.truncated })))
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const spawner = yield* ChildProcessSpawner
@ -256,7 +256,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(CrossSpawnSpawner.defaultLayer))
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [CrossSpawnSpawner.node] })
export * as AppProcess from "./process"

View file

@ -68,7 +68,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/ProjectV2") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@ -146,11 +146,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Git.defaultLayer),
Layer.provideMerge(ProjectDirectories.defaultLayer),
)
export const node = makeGlobalNode({
service: Service,
layer: layer,

View file

@ -125,7 +125,7 @@ export const refreshAfterBoot = Effect.gen(function* () {
)
})
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@ -278,7 +278,6 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer
export const node = makeLocationNode({
service: Service,
layer: layer,

View file

@ -48,7 +48,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/ProjectDirectories") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const db = (yield* Database.Service).db
@ -146,5 +146,4 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(Database.defaultLayer))
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [Database.node] })

View file

@ -89,7 +89,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Pty") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2.Service
@ -313,6 +313,4 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer.pipe(Layer.provide(Config.locationLayer))
export const node = makeLocationNode({ service: Service, layer, deps: [EventV2.node, Location.node, Config.node] })

View file

@ -51,7 +51,6 @@ export const make = (ttl: Duration.Input = DEFAULT_TTL) =>
})
})
export const layer = Layer.effect(Service, make())
const layer = Layer.effect(Service, make())
export const defaultLayer = layer
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [] })

View file

@ -72,7 +72,7 @@ interface Pending {
* layer once per embedded Location so replies cannot settle another Location's
* deferred request.
*/
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2.Service
@ -148,6 +148,4 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer
export const node = makeLocationNode({ service: Service, layer, deps: [EventV2.node] })

View file

@ -40,7 +40,7 @@ export interface Interface extends State.Transformable<Draft> {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Reference") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const global = yield* Global.Service
@ -120,8 +120,6 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer
export const node = makeLocationNode({
service: Service,
layer,

View file

@ -31,7 +31,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/ReferenceGuidance") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const references = yield* Reference.Service
@ -64,6 +64,4 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer
export const node = makeLocationNode({ service: Service, layer, deps: [Reference.node] })

View file

@ -121,7 +121,7 @@ export const validateBranch = Effect.fn("RepositoryCache.validateBranch")(functi
})
})
export const layer: Layer.Layer<Service, never, FSUtil.Service | Git.Service | EffectFlock.Service | Global.Service> =
const layer: Layer.Layer<Service, never, FSUtil.Service | Git.Service | EffectFlock.Service | Global.Service> =
Layer.effect(
Service,
Effect.gen(function* () {
@ -223,13 +223,6 @@ export const layer: Layer.Layer<Service, never, FSUtil.Service | Git.Service | E
}),
)
export const defaultLayer: Layer.Layer<Service> = layer.pipe(
Layer.provide(EffectFlock.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Git.defaultLayer),
Layer.provide(Global.defaultLayer),
)
export const node = makeGlobalNode({
service: Service,
layer,

View file

@ -89,7 +89,7 @@ const failure = (message: string, cause?: unknown) => new Error({ message, cause
const isInvalidPattern = (stderr: string) =>
stderr.includes("regex parse error") || stderr.includes("error parsing regex")
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const process = yield* AppProcess.Service
@ -278,5 +278,4 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(Layer.merge(RipgrepBinary.defaultLayer, AppProcess.defaultLayer)))
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [RipgrepBinary.node, AppProcess.node] })

View file

@ -28,7 +28,7 @@ export namespace RipgrepBinary {
export class Service extends Context.Service<Service, Interface>()("@opencode/RipgrepBinary") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@ -124,12 +124,6 @@ export namespace RipgrepBinary {
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(FetchHttpClient.layer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(CrossSpawnSpawner.defaultLayer),
)
export const node = makeGlobalNode({
service: Service,
layer: layer,

View file

@ -213,7 +213,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Session") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const database = yield* Database.Service
@ -584,16 +584,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(Job.defaultLayer),
Layer.provide(SessionStore.defaultLayer),
Layer.provide(SessionProjector.defaultLayer),
Layer.provide(EventV2.defaultLayer),
Layer.provide(Database.defaultLayer),
Layer.provide(ProjectV2.defaultLayer),
Layer.orDie,
)
const resolvePrompt = (input: PromptInput.Prompt) =>
Prompt.make({
text: input.text,

View file

@ -8,7 +8,7 @@ import { SessionStore } from "../store"
import { SessionExecution } from "../execution"
/** Current-process routing for implicit-local Locations. Future remote placement belongs here. */
export const layer = Layer.effect(
const layer = Layer.effect(
SessionExecution.Service,
Effect.gen(function* () {
const store = yield* SessionStore.Service
@ -38,8 +38,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(SessionStore.defaultLayer))
export const node = makeGlobalNode({
service: SessionExecution.Service,
layer,

View file

@ -407,7 +407,7 @@ function insertMessage(db: DatabaseService, event: SessionEvent.Event, message:
.pipe(Effect.orDie)
}
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const events = yield* EventV2.Service
const { db } = yield* Database.Service
@ -672,5 +672,4 @@ export const layer = Layer.effectDiscard(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(EventV2.defaultLayer), Layer.provide(Database.defaultLayer))
export const node = makeGlobalNode({ name: "session-projector", layer, deps: [EventV2.node, Database.node] })

View file

@ -10,6 +10,7 @@ import {
} from "@opencode-ai/llm"
import { Cause, DateTime, Effect, FiberSet, Layer, Option, Semaphore, Stream } from "effect"
import { AgentV2 } from "../../agent"
import { Config } from "../../config"
import { Database } from "../../database/database"
import { EventV2 } from "../../event"
import { Location } from "../../location"
@ -91,7 +92,7 @@ import { llmClient } from "../../effect/app-node-platform"
* explicit loop starts the next provider turn after local settlement. Configured agent step limits bound the loop.
*/
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2.Service
@ -423,8 +424,6 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer
export const node = makeLocationNode({
service: Service,
layer,
@ -442,6 +441,7 @@ export const node = makeLocationNode({
McpGuidance.node,
SessionCompaction.node,
SessionTitle.node,
Config.node,
Snapshot.node,
Database.node,
],

View file

@ -179,7 +179,7 @@ export const supported = (model: ModelV2.Info) =>
(model.api.package === "@ai-sdk/openai-compatible" && model.api.url !== undefined))
/** Resolves models from the catalog belonging to the current Location runtime. */
export const locationLayer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const catalog = yield* Catalog.Service
@ -215,4 +215,4 @@ export const locationLayer = Layer.effect(
}),
)
export const node = makeLocationNode({ service: Service, layer: locationLayer, deps: [Catalog.node, Integration.node] })
export const node = makeLocationNode({ service: Service, layer, deps: [Catalog.node, Integration.node] })

View file

@ -25,7 +25,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/SessionStore") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const { db } = yield* Database.Service
@ -60,6 +60,4 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(Database.defaultLayer))
export const node = makeGlobalNode({ service: Service, layer, deps: [Database.node] })

View file

@ -23,7 +23,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/SessionTodo") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const { db } = yield* Database.Service
@ -75,6 +75,4 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(EventV2.defaultLayer), Layer.provide(Database.defaultLayer))
export const node = makeLocationNode({ service: Service, layer, deps: [EventV2.node, Database.node] })

View file

@ -300,8 +300,6 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer.pipe(Layer.provide(Config.locationLayer))
export const node = makeLocationNode({
service: Service,
layer,

View file

@ -71,7 +71,7 @@ export interface Interface extends State.Transformable<Draft> {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Skill") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const discovery = yield* SkillDiscovery.Service
@ -180,6 +180,4 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer.pipe(Layer.provide(SkillDiscovery.defaultLayer))
export const node = makeLocationNode({ service: Service, layer, deps: [SkillDiscovery.node, FSUtil.node, EventV2.node] })

View file

@ -68,7 +68,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/SkillDiscovery") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@ -210,10 +210,4 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(
Layer.provide(FetchHttpClient.layer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Global.defaultLayer),
)
export const node = makeGlobalNode({ service: Service, layer, deps: [httpClient, FSUtil.node, Global.node] })

View file

@ -37,7 +37,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/SkillGuidance") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const skills = yield* SkillV2.Service
@ -73,6 +73,4 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer
export const node = makeLocationNode({ service: Service, layer, deps: [SkillV2.node] })

View file

@ -83,7 +83,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Snapshot") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const config = yield* Config.Service
@ -227,8 +227,6 @@ export const layer = Layer.effect(
}),
)
export const locationLayer = layer.pipe(Layer.provideMerge(Config.locationLayer))
export const node = makeLocationNode({
service: Service,
layer,

View file

@ -43,14 +43,8 @@ const builtIns = Layer.effectDiscard(
}),
)
export const layer = Layer.mergeAll(builtIns, InstructionContext.layer).pipe(
Layer.provideMerge(SystemContextRegistry.layer),
)
export const locationLayer = layer
export const node = makeLocationNode({
name: "system-context-builtins",
layer,
layer: builtIns,
deps: [Location.node, SystemContextRegistry.node, InstructionContext.node, FSUtil.node, Global.node],
})

View file

@ -16,7 +16,7 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/SystemContextRegistry") {}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const entries = yield* Ref.make<ReadonlyArray<Entry>>([])

View file

@ -109,7 +109,7 @@ const lineCount = (text: string) => {
return count
}
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service
@ -192,10 +192,10 @@ export const layer = Layer.effect(
}),
)
export const defaultLayer = layer.pipe(Layer.provide(FSUtil.defaultLayer), Layer.provide(Global.defaultLayer))
export const node = makeLocationNode({ service: Service, layer, deps: [FSUtil.node, Global.node, Config.node] })
export const nodeWithoutConfig = makeLocationNode({ service: Service, layer, deps: [FSUtil.node, Global.node] })
/** Runs retention scanning once globally rather than once per active Location. */
export const cleanupLayer = Layer.effectDiscard(
Effect.gen(function* () {
@ -204,6 +204,8 @@ export const cleanupLayer = Layer.effectDiscard(
}),
)
export const defaultCleanupLayer = Layer.merge(defaultLayer, cleanupLayer.pipe(Layer.provide(defaultLayer)))
export const cleanupNode = makeGlobalNode({ name: "tool-output-cleanup", layer: defaultCleanupLayer, deps: [] })
export const cleanupNode = makeGlobalNode({
name: "tool-output-cleanup",
layer: Layer.merge(layer, cleanupLayer.pipe(Layer.provide(layer))),
deps: [FSUtil.node, Global.node],
})

View file

@ -4,11 +4,13 @@ import { ToolFailure } from "@opencode-ai/llm"
import { FileDiff } from "@opencode-ai/schema/file-diff"
import { createTwoFilesPatch, diffLines } from "diff"
import { Effect, Layer, Schema } from "effect"
import { makeLocationNode } from "../effect/app-node"
import { FileMutation } from "../file-mutation"
import { FSUtil } from "../fs-util"
import { LocationMutation } from "../location-mutation"
import { Patch } from "../patch"
import { PermissionV2 } from "../permission"
import { ToolRegistry } from "./registry"
import { Tool } from "./tool"
import { Tools } from "./tools"
@ -54,7 +56,7 @@ type Prepared =
readonly after: string
})
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const tools = yield* Tools.Service
const mutation = yield* LocationMutation.Service
@ -194,6 +196,12 @@ export const layer = Layer.effectDiscard(
}),
)
export const node = makeLocationNode({
name: "tool/apply-patch",
layer,
deps: [ToolRegistry.node, LocationMutation.node, FileMutation.node, FSUtil.node, PermissionV2.node],
})
function patchFile(change: Prepared): typeof FileDiff.Info.Type {
const counts = diffLines(change.before, change.after).reduce(
(result, item) => ({

View file

@ -14,18 +14,6 @@ import { TodoWriteTool } from "./todowrite"
import { WebFetchTool } from "./webfetch"
import { WebSearchTool } from "./websearch"
import { WriteTool } from "./write"
import { FSUtil } from "../fs-util"
import { Location } from "../location"
import { LocationMutation } from "../location-mutation"
import { FileMutation } from "../file-mutation"
import { PermissionV2 } from "../permission"
import { Ripgrep } from "../ripgrep"
import { Image } from "../image"
import { QuestionV2 } from "../question"
import { SkillV2 } from "../skill"
import { SessionTodo } from "../session/todo"
import { ToolRegistry } from "./registry"
import { httpClient } from "../effect/app-node-platform"
export class Service extends Context.Service<Service, Record<string, never>>()("@opencode/v2/BuiltInTools") {}
@ -42,38 +30,24 @@ export class Service extends Context.Service<Service, Record<string, never>>()("
* repo_clone, repo_overview, plan_exit, and Rune/code mode. Keep MCP and plugin
* transforms separate from this static built-in list.
*/
const registrations = Layer.mergeAll(
ApplyPatchTool.layer,
EditTool.layer,
GlobTool.layer,
GrepTool.layer,
QuestionTool.layer,
ReadTool.layer.pipe(Layer.provide(ReadToolFileSystem.layer)),
SkillTool.layer,
TodoWriteTool.layer,
WebFetchTool.layer,
WebSearchTool.layer.pipe(Layer.provide(WebSearchTool.defaultConfigLayer)),
WriteTool.layer,
)
export const locationLayer = Layer.succeed(Service, Service.of({})).pipe(Layer.provideMerge(registrations))
const layer = Layer.succeed(Service, Service.of({}))
export const node = makeLocationNode({
service: Service,
layer: locationLayer,
layer,
deps: [
ToolRegistry.toolsNode,
FSUtil.node,
Location.node,
LocationMutation.node,
FileMutation.node,
PermissionV2.node,
Ripgrep.node,
Image.node,
QuestionV2.node,
SkillV2.node,
SessionTodo.node,
ApplyPatchTool.node,
EditTool.node,
GlobTool.node,
GrepTool.node,
QuestionTool.node,
ReadTool.node,
ReadToolFileSystem.node,
httpClient,
SkillTool.node,
TodoWriteTool.node,
WebFetchTool.node,
WebSearchTool.node,
WebSearchTool.configNode,
WriteTool.node,
],
})

View file

@ -10,10 +10,12 @@ import { ToolFailure } from "@opencode-ai/llm"
import { FileDiff } from "@opencode-ai/schema/file-diff"
import { createTwoFilesPatch, diffLines } from "diff"
import { Effect, Layer, Schema } from "effect"
import { makeLocationNode } from "../effect/app-node"
import { FileMutation } from "../file-mutation"
import { FSUtil } from "../fs-util"
import { LocationMutation } from "../location-mutation"
import { PermissionV2 } from "../permission"
import { ToolRegistry } from "./registry"
import { Tool } from "./tool"
import { Tools } from "./tools"
@ -85,7 +87,7 @@ export const toModelOutput = (output: Output, oldString: string, newString: stri
// TODO: Add snapshots / undo after design exists.
// TODO: Add LSP notification and diagnostics after V2 LSP runtime exists.
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const tools = yield* Tools.Service
const mutation = yield* LocationMutation.Service
@ -213,3 +215,9 @@ export const layer = Layer.effectDiscard(
.pipe(Effect.orDie)
}),
)
export const node = makeLocationNode({
name: "tool/edit",
layer,
deps: [ToolRegistry.node, LocationMutation.node, FileMutation.node, FSUtil.node, PermissionV2.node],
})

View file

@ -3,11 +3,13 @@ export * as GlobTool from "./glob"
import { ToolFailure } from "@opencode-ai/llm"
import { Effect, Layer, Schema } from "effect"
import path from "path"
import { makeLocationNode } from "../effect/app-node"
import { FileSystem } from "../filesystem"
import { Location } from "../location"
import { Ripgrep } from "../ripgrep"
import { RelativePath } from "../schema"
import { PermissionV2 } from "../permission"
import { ToolRegistry } from "./registry"
import { Tool } from "./tool"
import { Tools } from "./tools"
@ -33,7 +35,7 @@ export const toModelOutput = (output: ModelOutput) => {
}
/** Glob leaf that defaults its filesystem root to the active Location. */
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const tools = yield* Tools.Service
const ripgrep = yield* Ripgrep.Service
@ -95,3 +97,9 @@ export const layer = Layer.effectDiscard(
.pipe(Effect.orDie)
}),
)
export const node = makeLocationNode({
name: "tool/glob",
layer,
deps: [ToolRegistry.node, Ripgrep.node, Location.node, PermissionV2.node],
})

View file

@ -3,12 +3,14 @@ export * as GrepTool from "./grep"
import { ToolFailure } from "@opencode-ai/llm"
import { Effect, Layer, Schema } from "effect"
import path from "path"
import { makeLocationNode } from "../effect/app-node"
import { FileSystem } from "../filesystem"
import { FSUtil } from "../fs-util"
import { Location } from "../location"
import { PermissionV2 } from "../permission"
import { Ripgrep } from "../ripgrep"
import { RelativePath } from "../schema"
import { ToolRegistry } from "./registry"
import { Tool } from "./tool"
import { Tools } from "./tools"
@ -48,7 +50,7 @@ export const toModelOutput = (output: ModelOutput) => {
}
/** Grep leaf that defaults its filesystem root to the active Location. */
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const tools = yield* Tools.Service
const fs = yield* FSUtil.Service
@ -127,3 +129,9 @@ export const layer = Layer.effectDiscard(
.pipe(Effect.orDie)
}),
)
export const node = makeLocationNode({
name: "tool/grep",
layer,
deps: [ToolRegistry.node, FSUtil.node, Ripgrep.node, Location.node, PermissionV2.node],
})

View file

@ -2,8 +2,10 @@ export * as QuestionTool from "./question"
import { ToolFailure } from "@opencode-ai/llm"
import { Effect, Layer, Schema } from "effect"
import { makeLocationNode } from "../effect/app-node"
import { PermissionV2 } from "../permission"
import { QuestionV2 } from "../question"
import { ToolRegistry } from "./registry"
import { Tool } from "./tool"
import { Tools } from "./tools"
@ -42,7 +44,7 @@ export const toModelOutput = (
return `User has answered your questions: ${formatted}. You can now continue with the user's answers in mind.`
}
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const tools = yield* Tools.Service
const question = yield* QuestionV2.Service
@ -84,3 +86,9 @@ export const layer = Layer.effectDiscard(
.pipe(Effect.orDie)
}),
)
export const node = makeLocationNode({
name: "tool/question",
layer,
deps: [ToolRegistry.node, PermissionV2.node, QuestionV2.node],
})

View file

@ -351,7 +351,7 @@ export const list = Effect.fn("ReadTool.list")(function* (fs: FSUtil.Interface,
return new ListPage({ entries: selected, truncated, ...(truncated ? { next: offset + selected.length } : {}) })
})
export const layer = Layer.effect(
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const fs = yield* FSUtil.Service

View file

@ -2,12 +2,14 @@ export * as ReadTool from "./read"
import { ToolFailure } from "@opencode-ai/llm"
import { Effect, Layer, Schema } from "effect"
import { makeLocationNode } from "../effect/app-node"
import { FileSystem } from "../filesystem"
import { Image } from "../image"
import { LocationMutation } from "../location-mutation"
import { PermissionV2 } from "../permission"
import { AbsolutePath } from "../schema"
import { ReadToolFileSystem } from "./read-filesystem"
import { ToolRegistry } from "./registry"
import { Tool } from "./tool"
import { Tools } from "./tools"
@ -25,7 +27,7 @@ const LocationInput = Schema.Struct({
const Input = LocationInput
const Output = Schema.Union([FileSystem.Content, ReadToolFileSystem.TextPage, ReadToolFileSystem.ListPage])
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const tools = yield* Tools.Service
const reader = yield* ReadToolFileSystem.Service
@ -107,3 +109,9 @@ export const layer = Layer.effectDiscard(
.pipe(Effect.orDie)
}),
)
export const node = makeLocationNode({
name: "tool/read",
layer,
deps: [ToolRegistry.node, ReadToolFileSystem.node, LocationMutation.node, Image.node, PermissionV2.node],
})

View file

@ -130,7 +130,7 @@ const registryLayer = Layer.effect(
}),
)
export const layer = Layer.effect(
const layer = Layer.effect(
Tools.Service,
Service.use((registry) => Effect.succeed(Tools.Service.of({ register: registry.register }))),
).pipe(Layer.provideMerge(registryLayer))
@ -140,8 +140,6 @@ function whollyDisabled(action: string, rules: PermissionV2.Ruleset) {
return rule?.resource === "*" && rule.effect === "deny"
}
export const defaultLayer = layer.pipe(Layer.provide(ToolOutputStore.defaultLayer))
export const node = makeLocationNode({
service: Service,
layer,

View file

@ -3,9 +3,11 @@ export * as SkillTool from "./skill"
import path from "path"
import { ToolFailure } from "@opencode-ai/llm"
import { Effect, Layer, Schema } from "effect"
import { makeLocationNode } from "../effect/app-node"
import { FSUtil } from "../fs-util"
import { SkillV2 } from "../skill"
import { PermissionV2 } from "../permission"
import { ToolRegistry } from "./registry"
import { Tool } from "./tool"
import { Tools } from "./tools"
@ -52,7 +54,7 @@ export const toModelOutput = (skill: SkillV2.Info, files: ReadonlyArray<string>)
const unableToLoad = (name: string, error?: unknown) =>
new ToolFailure({ message: `Unable to load skill ${name}`, error })
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const tools = yield* Tools.Service
const fs = yield* FSUtil.Service
@ -99,3 +101,9 @@ export const layer = Layer.effectDiscard(
.pipe(Effect.orDie)
}),
)
export const node = makeLocationNode({
name: "tool/skill",
layer,
deps: [ToolRegistry.node, FSUtil.node, SkillV2.node, PermissionV2.node],
})

View file

@ -2,8 +2,10 @@ export * as TodoWriteTool from "./todowrite"
import { ToolFailure } from "@opencode-ai/llm"
import { Effect, Layer, Schema } from "effect"
import { makeLocationNode } from "../effect/app-node"
import { PermissionV2 } from "../permission"
import { SessionTodo } from "../session/todo"
import { ToolRegistry } from "./registry"
import { Tool } from "./tool"
import { Tools } from "./tools"
@ -20,7 +22,7 @@ export type Output = typeof Output.Type
export const toModelOutput = (output: Output) => JSON.stringify(output.todos, null, 2)
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const tools = yield* Tools.Service
const todos = yield* SessionTodo.Service
@ -52,3 +54,9 @@ export const layer = Layer.effectDiscard(
.pipe(Effect.orDie)
}),
)
export const node = makeLocationNode({
name: "tool/todowrite",
layer,
deps: [ToolRegistry.node, PermissionV2.node, SessionTodo.node],
})

View file

@ -5,8 +5,11 @@ import { Duration, Effect, Layer, Schema } from "effect"
import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
import { Parser } from "htmlparser2"
import TurndownService from "turndown"
import { makeLocationNode } from "../effect/app-node"
import { LayerNodePlatform } from "../effect/app-node-platform"
import { PermissionV2 } from "../permission"
import { collectBoundedResponseBody } from "./http-body"
import { ToolRegistry } from "./registry"
import { Tool } from "./tool"
import { Tools } from "./tools"
@ -112,7 +115,7 @@ const convert = (content: string, contentType: string, format: Format) => {
return content
}
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const tools = yield* Tools.Service
const http = yield* HttpClient.HttpClient
@ -177,6 +180,12 @@ export const layer = Layer.effectDiscard(
}),
)
export const node = makeLocationNode({
name: "tool/webfetch",
layer,
deps: [ToolRegistry.node, PermissionV2.node, LayerNodePlatform.httpClient],
})
export function extractTextFromHTML(html: string) {
let text = ""
let skipDepth = 0

View file

@ -3,6 +3,8 @@ export * as WebSearchTool from "./websearch"
import { ToolFailure } from "@opencode-ai/llm"
import { Context, Duration, Effect, Layer, Schema } from "effect"
import { HttpClient, HttpClientRequest } from "effect/unstable/http"
import { makeLocationNode } from "../effect/app-node"
import { LayerNodePlatform } from "../effect/app-node-platform"
import { truthy } from "../flag/flag"
import { InstallationVersion } from "../installation/version"
import { PositiveInt } from "../schema"
@ -11,6 +13,7 @@ import { Tool } from "./tool"
import { Tools } from "./tools"
import { collectBoundedResponseBody } from "./http-body"
import { checksum } from "../util/encode"
import { ToolRegistry } from "./registry"
export const name = "websearch"
export const NO_RESULTS = "No search results found. Please try a different query."
@ -80,6 +83,8 @@ export const defaultConfigLayer = Layer.sync(ConfigService, () =>
}),
)
export const configNode = makeLocationNode({ service: ConfigService, layer: defaultConfigLayer, deps: [] })
export function selectProvider(
sessionID: string,
flags: Pick<Config, "enableExa" | "enableParallel"> = { enableExa: false, enableParallel: false },
@ -184,7 +189,7 @@ const Output = Schema.Struct({
text: Schema.String,
})
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const tools = yield* Tools.Service
const http = yield* HttpClient.HttpClient
@ -247,3 +252,9 @@ export const layer = Layer.effectDiscard(
.pipe(Effect.orDie)
}),
)
export const node = makeLocationNode({
name: "tool/websearch",
layer,
deps: [ToolRegistry.node, PermissionV2.node, LayerNodePlatform.httpClient, configNode],
})

View file

@ -8,9 +8,11 @@ export * as WriteTool from "./write"
import { ToolFailure } from "@opencode-ai/llm"
import { Effect, Layer, Schema } from "effect"
import { makeLocationNode } from "../effect/app-node"
import { FileMutation } from "../file-mutation"
import { LocationMutation } from "../location-mutation"
import { PermissionV2 } from "../permission"
import { ToolRegistry } from "./registry"
import { Tool } from "./tool"
import { Tools } from "./tools"
@ -42,7 +44,7 @@ export const toModelOutput = (output: Output) =>
// TODO: Add snapshots / undo after design exists.
// TODO: Add LSP notification and diagnostics after V2 LSP runtime exists.
export const layer = Layer.effectDiscard(
const layer = Layer.effectDiscard(
Effect.gen(function* () {
const tools = yield* Tools.Service
const mutation = yield* LocationMutation.Service
@ -91,3 +93,9 @@ export const layer = Layer.effectDiscard(
.pipe(Effect.orDie)
}),
)
export const node = makeLocationNode({
name: "tool/write",
layer,
deps: [ToolRegistry.node, LocationMutation.node, FileMutation.node, PermissionV2.node],
})

View file

@ -94,7 +94,7 @@ export namespace EffectFlock {
const isPathGone = (e: PlatformError) => e.reason._tag === "NotFound" || e.reason._tag === "Unknown"
export const layer: Layer.Layer<Service, never, Global.Service | FSUtil.Service> = Layer.effect(
const layer: Layer.Layer<Service, never, Global.Service | FSUtil.Service> = Layer.effect(
Service,
Effect.gen(function* () {
const global = yield* Global.Service
@ -280,6 +280,5 @@ export namespace EffectFlock {
}),
)
export const defaultLayer = layer.pipe(Layer.provide(FSUtil.defaultLayer), Layer.provide(Global.layer))
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [Global.node, FSUtil.node] })
}