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
@@ -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] })
+2 -4
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: [] })
+1 -6
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] })
+1 -3
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: [] })
+1 -3
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,
@@ -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],
})
+1 -3
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] })
+1 -2
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"
+1 -7
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: [] })
+11 -12
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"
+1
View File
@@ -0,0 +1 @@
File to save in: ~/.local/share/opencode/worktree/012780/location-layer-tiers/packages/core/src/effect/
+116 -56
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]
}
-2
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))
+1 -3
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] })
/**
-4
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,
-2
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] })
+1 -3
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,
+1 -2
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)
+1 -2
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 {
@@ -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
@@ -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({
+1 -2
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>) =>
+1 -3
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] })
+1 -1
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
+2 -2
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] })
+1 -3
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: [] })
+1 -3
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),
+6 -7
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" },
+1 -1
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* () {
-2
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] })
+2 -7
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"
+3 -8
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))
+3
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: [] })
+1 -3
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,
+1 -3
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] })
+1 -13
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,
+1 -8
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,
+3 -3
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(() => {
+1 -3
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] })
+1 -2
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"
+1 -6
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,
+1 -2
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,
+1 -2
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] })
+1 -3
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] })
+1 -2
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: [] })
+1 -3
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] })
+1 -3
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,
+1 -3
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] })
+1 -8
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,
+1 -2
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] })
+1 -7
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,
+1 -11
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,
+1 -3
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,
+1 -2
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] })
+3 -3
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,
],
+2 -2
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] })
+1 -3
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] })
+1 -3
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] })
-2
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,
+1 -3
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] })
+1 -7
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] })
+1 -3
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] })
+1 -3
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,
+1 -7
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],
})
+1 -1
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>>([])
+8 -6
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],
})
+9 -1
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) => ({
+14 -40
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,
],
})
+9 -1
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],
})
+9 -1
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],
})
+9 -1
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],
})
+9 -1
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],
})
+1 -1
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
+9 -1
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],
})
+1 -3
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,
+9 -1
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],
})
+9 -1
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],
})
+10 -1
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
+12 -1
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],
})
+9 -1
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],
})
+1 -2
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] })
}