mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
fix(core): ignore blank subagent options (#50977)
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
f53cc41657
commit
757e565c23
@@ -3,7 +3,7 @@ export * as SubagentTool from "./subagent.js"
|
||||
import { ToolFailure } from "@opencode/ai"
|
||||
import type { Context } from "@opencode/plugin/effect/plugin"
|
||||
import type { SessionHooks } from "@opencode/plugin/effect/session"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { Effect, Predicate, Schema } from "effect"
|
||||
import { Agent } from "../../agent.js"
|
||||
import { Config } from "../../config.js"
|
||||
import { Job } from "../../job.js"
|
||||
@@ -268,6 +268,17 @@ export const Plugin = {
|
||||
)
|
||||
.pipe(Effect.orDie)
|
||||
|
||||
yield* ctx.tool.hook("execute.before", (event) =>
|
||||
Effect.sync(() => {
|
||||
if (event.tool !== name || !Predicate.isObject(event.input)) return
|
||||
if (event.input.model !== "" && event.input.sessionID !== "") return
|
||||
const input = { ...event.input }
|
||||
if (input.model === "") delete input.model
|
||||
if (input.sessionID === "") delete input.sessionID
|
||||
event.input = input
|
||||
}),
|
||||
)
|
||||
|
||||
const hook = (event: SessionHooks["context"]) =>
|
||||
Effect.gen(function* () {
|
||||
const tool = event.tools[name]
|
||||
|
||||
@@ -60,6 +60,7 @@ export const registerToolPlugin = <R>(
|
||||
readonly effect: (context: Context) => Effect.Effect<void, never, R>
|
||||
},
|
||||
overrides: Parameters<typeof host>[0] = {},
|
||||
hook?: Context["tool"]["hook"],
|
||||
): Effect.Effect<void, never, R | Tool.Service | Scope.Scope> =>
|
||||
Effect.gen(function* () {
|
||||
const tools = yield* Tool.Service
|
||||
@@ -73,7 +74,7 @@ export const registerToolPlugin = <R>(
|
||||
transform: tools.transform,
|
||||
reload: tools.reload,
|
||||
list: tools.list,
|
||||
hook: () => Effect.die("registerToolPlugin does not support tool hooks"),
|
||||
hook: hook ?? (() => Effect.die("registerToolPlugin does not support tool hooks")),
|
||||
},
|
||||
})
|
||||
yield* plugin.effect(context)
|
||||
|
||||
@@ -30,6 +30,7 @@ import { SessionMessage } from "@opencode/core/session/message"
|
||||
import { SessionRunnerModel } from "@opencode/core/session/runner/model"
|
||||
import { SessionStore } from "@opencode/core/session/store"
|
||||
import { Plugin } from "@opencode/core/plugin"
|
||||
import { PluginHooks } from "@opencode/core/plugin/hooks"
|
||||
import { PluginSupervisor } from "@opencode/core/plugin/supervisor"
|
||||
import { Permission } from "@opencode/core/permission"
|
||||
import { SubagentTool } from "@opencode/core/tool/plugin/subagent"
|
||||
@@ -112,8 +113,13 @@ const executionNode = makeGlobalNode({
|
||||
|
||||
const subagentPluginSupervisor = makeLocationNode({
|
||||
name: "test/subagent-plugins",
|
||||
layer: Layer.effectDiscard(registerToolPlugin(SubagentTool.Plugin)),
|
||||
deps: [Agent.node, Config.node, Model.node, Permission.node, Session.node, Job.node, Tool.node],
|
||||
layer: Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const hooks = yield* PluginHooks.Service
|
||||
yield* registerToolPlugin(SubagentTool.Plugin, {}, (name, callback) => hooks.register("tool", name, callback))
|
||||
}),
|
||||
),
|
||||
deps: [Agent.node, Config.node, Model.node, Permission.node, Session.node, Job.node, Tool.node, PluginHooks.node],
|
||||
})
|
||||
|
||||
const nodes = LayerNode.group([
|
||||
@@ -407,7 +413,7 @@ describe("SubagentTool", () => {
|
||||
type: "tool-call",
|
||||
id: "call-subagent",
|
||||
name: SubagentTool.name,
|
||||
input: { agent: "reviewer", description: "review", prompt: "review this" },
|
||||
input: { agent: "reviewer", description: "review", prompt: "review this", model: "", sessionID: "" },
|
||||
},
|
||||
})
|
||||
|
||||
@@ -484,6 +490,7 @@ describe("SubagentTool", () => {
|
||||
description: "follow up",
|
||||
prompt: "continue this",
|
||||
sessionID: childID,
|
||||
model: "",
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -600,6 +607,28 @@ describe("SubagentTool", () => {
|
||||
message: `Subagent session not found: ${missing}`,
|
||||
},
|
||||
})
|
||||
expect(
|
||||
yield* executeTool(registry, {
|
||||
sessionID: parent.id,
|
||||
...toolIdentity,
|
||||
call: {
|
||||
type: "tool-call",
|
||||
id: "call-malformed-child-id",
|
||||
name: SubagentTool.name,
|
||||
input: { agent: "reviewer", description: "follow up", prompt: "continue", sessionID: "placeholder" },
|
||||
},
|
||||
}),
|
||||
).toMatchObject({
|
||||
status: "error",
|
||||
error: { message: expect.stringContaining('sessionID: Expected a string starting with "ses"') },
|
||||
})
|
||||
expect(yield* call(parent.id, "call-parent-id")).toEqual({
|
||||
status: "error",
|
||||
error: {
|
||||
type: "tool.execution",
|
||||
message: `Session ${parent.id} is not a child of the current session`,
|
||||
},
|
||||
})
|
||||
expect(yield* call(unrelated.id, "call-unrelated-child")).toEqual({
|
||||
status: "error",
|
||||
error: {
|
||||
@@ -655,7 +684,7 @@ describe("SubagentTool", () => {
|
||||
})
|
||||
|
||||
// The requested model beats the agent's configured model.
|
||||
const spawned = yield* call("call-override", { model: "test/override#fast" })
|
||||
const spawned = yield* call("call-override", { model: "test/override#fast", sessionID: "" })
|
||||
expect(spawned).toMatchObject({ status: "completed", metadata: { status: "completed" } })
|
||||
const child = yield* sessions.get(outputSessionID(spawned.metadata))
|
||||
expect(child).toMatchObject({ agent: "reviewer", model: overrideModel })
|
||||
|
||||
Reference in New Issue
Block a user