refactor(core): consolidate tool architecture
This commit is contained in:
parent
0fd73a2976
commit
8db7487c89
466 changed files with 9405 additions and 11071 deletions
|
|
@ -9,48 +9,47 @@ import { LayerNode } from "@opencode-ai/util/effect/layer-node"
|
|||
import { makeGlobalNode } from "@opencode-ai/util/effect/app-node"
|
||||
import { filesystem } from "@opencode-ai/util/effect/app-node-platform"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { Bus } from "@opencode-ai/core/bus"
|
||||
import { FSUtil } from "@opencode-ai/util/fs-util"
|
||||
import { Global } from "@opencode-ai/util/global"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { LocationServiceMap } from "@opencode-ai/core/location-service-map"
|
||||
import { ModelV2 } from "@opencode-ai/core/model"
|
||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||
import { Model } from "@opencode-ai/core/model"
|
||||
import { Provider } from "@opencode-ai/core/provider"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||
import { Agent } from "@opencode-ai/core/agent"
|
||||
import { Job } from "@opencode-ai/core/job"
|
||||
import { SessionV2 } from "@opencode-ai/core/session"
|
||||
import { Session } from "@opencode-ai/core/session"
|
||||
import { SessionEvent } from "@opencode-ai/core/session/event"
|
||||
import { SessionExecution } from "@opencode-ai/core/session/execution"
|
||||
import { SessionMessage } from "@opencode-ai/core/session/message"
|
||||
import { SessionStore } from "@opencode-ai/core/session/store"
|
||||
import { PermissionV2 } from "@opencode-ai/core/permission"
|
||||
import { Permission } from "@opencode-ai/core/permission"
|
||||
import { PluginRuntime } from "@opencode-ai/core/plugin/runtime"
|
||||
import { Shell } from "@opencode-ai/core/shell"
|
||||
import { Shell as ShellSchema } from "@opencode-ai/schema/shell"
|
||||
import { ShellTool } from "@opencode-ai/core/tool/shell"
|
||||
import { ToolRegistry } from "@opencode-ai/core/tool/registry"
|
||||
import { ToolOutputStore } from "@opencode-ai/core/tool-output-store"
|
||||
import { ShellTool } from "@opencode-ai/core/tool/plugin/shell"
|
||||
import { Tool } from "@opencode-ai/core/tool"
|
||||
import { tmpdir } from "./fixture/tmpdir"
|
||||
import { testEffect } from "./lib/effect"
|
||||
import { toolIdentity, executeTool, toolDefinitions, waitForTool } from "./lib/tool"
|
||||
|
||||
const sessionID = SessionV2.ID.make("ses_shell_tool_test")
|
||||
const sessionModel = ModelV2.Ref.make({ id: ModelV2.ID.make("test"), providerID: ProviderV2.ID.make("test") })
|
||||
const assertions: PermissionV2.AssertInput[] = []
|
||||
const sessionID = Session.ID.make("ses_shell_tool_test")
|
||||
const sessionModel = Model.Ref.make({ id: Model.ID.make("test"), providerID: Provider.ID.make("test") })
|
||||
const assertions: Permission.AssertInput[] = []
|
||||
let denyAction: string | undefined
|
||||
let afterPermission = (_input: PermissionV2.AssertInput): Effect.Effect<void> => Effect.void
|
||||
let afterPermission = (_input: Permission.AssertInput): Effect.Effect<void> => Effect.void
|
||||
|
||||
const permission = Layer.succeed(
|
||||
PermissionV2.Service,
|
||||
PermissionV2.Service.of({
|
||||
Permission.Service,
|
||||
Permission.Service.of({
|
||||
assert: (input) =>
|
||||
Effect.sync(() => assertions.push(input)).pipe(
|
||||
Effect.andThen(Effect.suspend(() => afterPermission(input))),
|
||||
Effect.andThen(
|
||||
input.action === denyAction
|
||||
? Effect.fail(
|
||||
new PermissionV2.BlockedError({
|
||||
new Permission.BlockedError({
|
||||
rules: [],
|
||||
permission: input.action,
|
||||
resources: input.resources,
|
||||
|
|
@ -78,30 +77,30 @@ const executionNode = makeGlobalNode({
|
|||
layer: Layer.effect(
|
||||
SessionExecution.Service,
|
||||
Effect.gen(function* () {
|
||||
const events = yield* EventV2.Service
|
||||
const bus = yield* Bus.Service
|
||||
const store = yield* SessionStore.Service
|
||||
const complete = Effect.fn("ShellTest.complete")(function* (id: SessionV2.ID) {
|
||||
const complete = Effect.fn("ShellTest.complete")(function* (id: Session.ID) {
|
||||
const session = yield* store.get(id)
|
||||
if (!session) return
|
||||
const assistantMessageID = SessionMessage.ID.create()
|
||||
yield* events.publish(SessionEvent.Step.Started, {
|
||||
yield* bus.publish(SessionEvent.Step.Started, {
|
||||
sessionID: id,
|
||||
assistantMessageID,
|
||||
agent: session.agent ?? AgentV2.ID.make("code"),
|
||||
agent: session.agent ?? Agent.ID.make("code"),
|
||||
model: sessionModel,
|
||||
})
|
||||
yield* events.publish(SessionEvent.Text.Started, {
|
||||
yield* bus.publish(SessionEvent.Text.Started, {
|
||||
sessionID: id,
|
||||
assistantMessageID,
|
||||
ordinal: 0,
|
||||
})
|
||||
yield* events.publish(SessionEvent.Text.Ended, {
|
||||
yield* bus.publish(SessionEvent.Text.Ended, {
|
||||
sessionID: id,
|
||||
assistantMessageID,
|
||||
ordinal: 0,
|
||||
text: "ok",
|
||||
})
|
||||
yield* events.publish(SessionEvent.Step.Ended, {
|
||||
yield* bus.publish(SessionEvent.Step.Ended, {
|
||||
sessionID: id,
|
||||
assistantMessageID,
|
||||
finish: "stop",
|
||||
|
|
@ -118,16 +117,15 @@ const executionNode = makeGlobalNode({
|
|||
})
|
||||
}),
|
||||
),
|
||||
deps: [EventV2.node, SessionStore.node],
|
||||
deps: [Bus.node, SessionStore.node],
|
||||
})
|
||||
|
||||
const layer = AppNodeBuilder.build(
|
||||
LayerNode.group([
|
||||
Database.node,
|
||||
EventV2.node,
|
||||
Bus.node,
|
||||
Job.node,
|
||||
ToolOutputStore.cleanupNode,
|
||||
SessionV2.node,
|
||||
Session.node,
|
||||
SessionExecution.node,
|
||||
PluginRuntime.providerNode,
|
||||
LocationServiceMap.node,
|
||||
|
|
@ -137,7 +135,7 @@ const layer = AppNodeBuilder.build(
|
|||
]),
|
||||
[
|
||||
[SessionExecution.node, executionNode],
|
||||
[PermissionV2.node, permission],
|
||||
[Permission.node, permission],
|
||||
],
|
||||
)
|
||||
|
||||
|
|
@ -174,9 +172,9 @@ const progressOverflowCommand = (bytes: number, release: string) =>
|
|||
? `[Console]::Out.Write(('x' * ${bytes})); while (!(Test-Path -LiteralPath '${release}')) { Start-Sleep -Milliseconds 50 }`
|
||||
: `head -c ${bytes} /dev/zero | tr '\\0' 'x'; while [ ! -e '${release}' ]; do sleep 0.05; done`
|
||||
|
||||
const withSession = <A, E, R>(directory: string, body: (registry: ToolRegistry.Interface) => Effect.Effect<A, E, R>) =>
|
||||
const withSession = <A, E, R>(directory: string, body: (registry: Tool.Interface) => Effect.Effect<A, E, R>) =>
|
||||
Effect.gen(function* () {
|
||||
const sessions = yield* SessionV2.Service
|
||||
const sessions = yield* Session.Service
|
||||
const location = Location.Ref.make({ directory: AbsolutePath.make(directory) })
|
||||
yield* sessions.create({
|
||||
id: sessionID,
|
||||
|
|
@ -187,7 +185,7 @@ const withSession = <A, E, R>(directory: string, body: (registry: ToolRegistry.I
|
|||
const locations = yield* LocationServiceMap.Service
|
||||
const locationLayer = locations.get(location)
|
||||
return yield* Effect.gen(function* () {
|
||||
const registry = yield* ToolRegistry.Service
|
||||
const registry = yield* Tool.Service
|
||||
yield* waitForTool(registry, ShellTool.name)
|
||||
return yield* body(registry)
|
||||
}).pipe(Effect.provide(locationLayer), Effect.ensuring(locations.invalidate(location)))
|
||||
|
|
@ -466,7 +464,7 @@ describe("ShellTool", () => {
|
|||
reset()
|
||||
return withSession(tmp.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
const updates: ToolRegistry.Progress[] = []
|
||||
const updates: Tool.Metadata[] = []
|
||||
yield* executeTool(registry, {
|
||||
...call({ command: steadyProgressCommand }, "call-steady-progress"),
|
||||
progress: (update) => Effect.sync(() => updates.push(update)),
|
||||
|
|
@ -511,8 +509,8 @@ describe("ShellTool", () => {
|
|||
reset()
|
||||
return withSession(tmp.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
const events = yield* EventV2.Service
|
||||
const admitted = yield* events.subscribe(SessionEvent.InputAdmitted).pipe(
|
||||
const bus = yield* Bus.Service
|
||||
const admitted = yield* bus.subscribe(SessionEvent.InputAdmitted).pipe(
|
||||
Stream.filter((event) => event.data.sessionID === sessionID && event.data.input.type === "synthetic"),
|
||||
Stream.runHead,
|
||||
Effect.forkScoped({ startImmediately: true }),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue