Revert "feat(core): add session rename tool"
This reverts commit 1b78dd7f9c.
This commit is contained in:
parent
b20d62de6a
commit
7c78d5ca44
4 changed files with 2 additions and 248 deletions
|
|
@ -43,7 +43,6 @@ import { QuestionTool } from "../tool/plugin/question"
|
||||||
import { ReadToolFileSystem } from "../tool/read-filesystem"
|
import { ReadToolFileSystem } from "../tool/read-filesystem"
|
||||||
import { ReadTool } from "../tool/plugin/read"
|
import { ReadTool } from "../tool/plugin/read"
|
||||||
import { ShellTool } from "../tool/plugin/shell"
|
import { ShellTool } from "../tool/plugin/shell"
|
||||||
import { SessionRenameTool } from "../tool/plugin/session-rename"
|
|
||||||
import { SkillTool } from "../tool/plugin/skill"
|
import { SkillTool } from "../tool/plugin/skill"
|
||||||
import { SubagentTool } from "../tool/plugin/subagent"
|
import { SubagentTool } from "../tool/plugin/subagent"
|
||||||
import { Tool } from "../tool"
|
import { Tool } from "../tool"
|
||||||
|
|
@ -150,7 +149,6 @@ const pre = [
|
||||||
QuestionTool.Plugin,
|
QuestionTool.Plugin,
|
||||||
ReadTool.Plugin,
|
ReadTool.Plugin,
|
||||||
ShellTool.Plugin,
|
ShellTool.Plugin,
|
||||||
SessionRenameTool.Plugin,
|
|
||||||
SkillTool.Plugin,
|
SkillTool.Plugin,
|
||||||
SubagentTool.Plugin,
|
SubagentTool.Plugin,
|
||||||
WebFetchTool.Plugin,
|
WebFetchTool.Plugin,
|
||||||
|
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
export * as SessionRenameTool from "./session-rename"
|
|
||||||
|
|
||||||
import type { Context as PluginContext } from "@opencode-ai/plugin/effect/plugin"
|
|
||||||
import { ToolFailure } from "@opencode-ai/ai"
|
|
||||||
import { Effect, Schema } from "effect"
|
|
||||||
import { Permission } from "../../permission"
|
|
||||||
import { PluginRuntime } from "../../plugin/runtime"
|
|
||||||
|
|
||||||
export const name = "sessionRename"
|
|
||||||
|
|
||||||
export const description =
|
|
||||||
"Rename the current session. Use a short, specific title that summarizes the work being done. This tool can only rename the session you are currently working in."
|
|
||||||
|
|
||||||
export const Input = Schema.Struct({
|
|
||||||
title: Schema.String.check(
|
|
||||||
Schema.isMinLength(1, { message: "Title must not be empty" }),
|
|
||||||
Schema.isMaxLength(100, { message: "Title must be 100 characters or fewer" }),
|
|
||||||
).annotate({ description: "New title for the current session" }),
|
|
||||||
})
|
|
||||||
|
|
||||||
export const Output = Schema.Struct({
|
|
||||||
title: Schema.String,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const Plugin = {
|
|
||||||
id: "opencode.tool.session-rename",
|
|
||||||
effect: Effect.fn("SessionRenameTool.Plugin")(function* (ctx: PluginContext) {
|
|
||||||
const runtime = yield* PluginRuntime.Service
|
|
||||||
const permission = yield* Permission.Service
|
|
||||||
|
|
||||||
yield* ctx.tool
|
|
||||||
.transform((draft) =>
|
|
||||||
draft.add(
|
|
||||||
({
|
|
||||||
name,
|
|
||||||
options: { codemode: false },
|
|
||||||
description,
|
|
||||||
input: Input,
|
|
||||||
output: Output,
|
|
||||||
execute: (input, context) =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const title = input.title.replace(/\s+/g, " ").trim()
|
|
||||||
if (!title) return yield* new ToolFailure({ message: "Session title must not be empty" })
|
|
||||||
yield* permission
|
|
||||||
.assert({
|
|
||||||
action: name,
|
|
||||||
resources: ["*"],
|
|
||||||
save: ["*"],
|
|
||||||
metadata: { title },
|
|
||||||
sessionID: context.sessionID,
|
|
||||||
agent: context.agent,
|
|
||||||
source: { type: "tool", messageID: context.messageID, callID: context.callID },
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
Effect.mapError(
|
|
||||||
(error) => new ToolFailure({ message: "Permission denied: sessionRename", error }),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
yield* runtime.session
|
|
||||||
.rename({ sessionID: context.sessionID, title })
|
|
||||||
.pipe(
|
|
||||||
Effect.mapError(
|
|
||||||
(error) => new ToolFailure({ message: "Unable to rename the current session", error }),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
output: { title },
|
|
||||||
content: `Renamed the current session to: ${title}`,
|
|
||||||
metadata: { title },
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.pipe(Effect.orDie)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
@ -552,9 +552,8 @@ describe("LocationServiceMap", () => {
|
||||||
"grep",
|
"grep",
|
||||||
"question",
|
"question",
|
||||||
"read",
|
"read",
|
||||||
"shell",
|
"shell",
|
||||||
"sessionRename",
|
"skill",
|
||||||
"skill",
|
|
||||||
"subagent",
|
"subagent",
|
||||||
"webfetch",
|
"webfetch",
|
||||||
"websearch",
|
"websearch",
|
||||||
|
|
@ -586,7 +585,6 @@ describe("LocationServiceMap", () => {
|
||||||
"patch",
|
"patch",
|
||||||
"question",
|
"question",
|
||||||
"read",
|
"read",
|
||||||
"sessionRename",
|
|
||||||
"shell",
|
"shell",
|
||||||
"skill",
|
"skill",
|
||||||
"subagent",
|
"subagent",
|
||||||
|
|
@ -606,7 +604,6 @@ describe("LocationServiceMap", () => {
|
||||||
"patch",
|
"patch",
|
||||||
"question",
|
"question",
|
||||||
"read",
|
"read",
|
||||||
"sessionRename",
|
|
||||||
"shell",
|
"shell",
|
||||||
"skill",
|
"skill",
|
||||||
"subagent",
|
"subagent",
|
||||||
|
|
|
||||||
|
|
@ -1,164 +0,0 @@
|
||||||
import { describe, expect } from "bun:test"
|
|
||||||
import { Effect, Layer } from "effect"
|
|
||||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
|
||||||
import { Agent } from "@opencode-ai/core/agent"
|
|
||||||
import { Image } from "@opencode-ai/core/image"
|
|
||||||
import { Permission } from "@opencode-ai/core/permission"
|
|
||||||
import { PluginRuntime } from "@opencode-ai/core/plugin/runtime"
|
|
||||||
import { Session } from "@opencode-ai/core/session"
|
|
||||||
import { Tool } from "@opencode-ai/core/tool"
|
|
||||||
import { SessionRenameTool } from "@opencode-ai/core/tool/plugin/session-rename"
|
|
||||||
import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
|
|
||||||
import { LayerNode } from "@opencode-ai/util/effect/layer-node"
|
|
||||||
import { imagePassthrough } from "./lib/image"
|
|
||||||
import { testEffect } from "./lib/effect"
|
|
||||||
import { executeTool, registerToolPlugin, toolDefinitions, toolIdentity } from "./lib/tool"
|
|
||||||
|
|
||||||
const currentSessionID = Session.ID.make("ses_session_rename_current")
|
|
||||||
const otherSessionID = Session.ID.make("ses_session_rename_other")
|
|
||||||
const renamed: Array<{ sessionID: Session.ID; title: string }> = []
|
|
||||||
const assertions: Permission.AssertInput[] = []
|
|
||||||
let deny = false
|
|
||||||
|
|
||||||
const runtime = Layer.succeed(
|
|
||||||
PluginRuntime.Service,
|
|
||||||
PluginRuntime.Service.of({
|
|
||||||
session: {
|
|
||||||
get: () => Effect.die("unused"),
|
|
||||||
create: () => Effect.die("unused"),
|
|
||||||
messages: () => Effect.die("unused"),
|
|
||||||
prompt: () => Effect.die("unused"),
|
|
||||||
generate: () => Effect.die("unused"),
|
|
||||||
command: () => Effect.die("unused"),
|
|
||||||
resume: () => Effect.die("unused"),
|
|
||||||
interrupt: () => Effect.die("unused"),
|
|
||||||
synthetic: () => Effect.die("unused"),
|
|
||||||
rename: (input) => Effect.sync(() => void renamed.push(input)),
|
|
||||||
wait: () => Effect.die("unused"),
|
|
||||||
},
|
|
||||||
job: {
|
|
||||||
start: () => Effect.die("unused"),
|
|
||||||
wait: () => Effect.die("unused"),
|
|
||||||
block: () => Effect.die("unused"),
|
|
||||||
background: () => Effect.die("unused"),
|
|
||||||
cancel: () => Effect.die("unused"),
|
|
||||||
},
|
|
||||||
location: {
|
|
||||||
agent: {
|
|
||||||
list: () => Effect.die("unused"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const permission = Layer.succeed(
|
|
||||||
Permission.Service,
|
|
||||||
Permission.Service.of({
|
|
||||||
assert: (input) =>
|
|
||||||
Effect.sync(() => assertions.push(input)).pipe(
|
|
||||||
Effect.andThen(
|
|
||||||
deny
|
|
||||||
? Effect.fail(
|
|
||||||
new Permission.BlockedError({
|
|
||||||
rules: [],
|
|
||||||
permission: input.action,
|
|
||||||
resources: input.resources,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
: Effect.void,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ask: () => Effect.die("unused"),
|
|
||||||
reply: () => Effect.die("unused"),
|
|
||||||
get: () => Effect.die("unused"),
|
|
||||||
forSession: () => Effect.die("unused"),
|
|
||||||
list: () => Effect.die("unused"),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const sessionRenameToolNode = makeLocationNode({
|
|
||||||
name: "test/session-rename-tool-plugin",
|
|
||||||
layer: Layer.effectDiscard(registerToolPlugin(SessionRenameTool.Plugin)),
|
|
||||||
deps: [Tool.node, Permission.node, PluginRuntime.node],
|
|
||||||
})
|
|
||||||
|
|
||||||
const it = testEffect(
|
|
||||||
AppNodeBuilder.build(LayerNode.group([Tool.node, sessionRenameToolNode]), [
|
|
||||||
[Permission.node, permission],
|
|
||||||
[PluginRuntime.node, runtime],
|
|
||||||
[Image.node, imagePassthrough],
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
|
|
||||||
describe("SessionRenameTool", () => {
|
|
||||||
it.effect("registers directly and confines rename to the invocation session", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
renamed.length = 0
|
|
||||||
assertions.length = 0
|
|
||||||
deny = false
|
|
||||||
const registry = yield* Tool.Service
|
|
||||||
const snapshot = yield* registry.snapshot()
|
|
||||||
|
|
||||||
expect(snapshot.definitions.map((tool) => tool.name)).toEqual(["sessionRename", "execute"])
|
|
||||||
expect(snapshot.codeModeCatalog?.some((tool) => tool.path === "sessionRename")).toBe(false)
|
|
||||||
expect(
|
|
||||||
yield* executeTool(registry, {
|
|
||||||
sessionID: currentSessionID,
|
|
||||||
...toolIdentity,
|
|
||||||
call: {
|
|
||||||
type: "tool-call",
|
|
||||||
id: "call-session-rename",
|
|
||||||
name: "sessionRename",
|
|
||||||
input: { title: "\n Focused\n title\t", sessionID: otherSessionID },
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
).toEqual({
|
|
||||||
status: "completed",
|
|
||||||
output: { title: "Focused title" },
|
|
||||||
content: [{ type: "text", text: "Renamed the current session to: Focused title" }],
|
|
||||||
metadata: { title: "Focused title" },
|
|
||||||
})
|
|
||||||
expect(renamed).toEqual([{ sessionID: currentSessionID, title: "Focused title" }])
|
|
||||||
expect(assertions).toMatchObject([
|
|
||||||
{
|
|
||||||
action: "sessionRename",
|
|
||||||
resources: ["*"],
|
|
||||||
sessionID: currentSessionID,
|
|
||||||
agent: Agent.ID.make("build"),
|
|
||||||
source: { type: "tool", messageID: toolIdentity.messageID, callID: "call-session-rename" },
|
|
||||||
},
|
|
||||||
])
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.effect("filters catalog denial and enforces leaf permission", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
renamed.length = 0
|
|
||||||
deny = true
|
|
||||||
const registry = yield* Tool.Service
|
|
||||||
|
|
||||||
expect(
|
|
||||||
(yield* toolDefinitions(registry, [{ action: "sessionRename", resource: "*", effect: "deny" }])).map(
|
|
||||||
(tool) => tool.name,
|
|
||||||
),
|
|
||||||
).toEqual(["execute"])
|
|
||||||
expect(
|
|
||||||
yield* executeTool(registry, {
|
|
||||||
sessionID: currentSessionID,
|
|
||||||
...toolIdentity,
|
|
||||||
call: {
|
|
||||||
type: "tool-call",
|
|
||||||
id: "call-session-rename-denied",
|
|
||||||
name: "sessionRename",
|
|
||||||
input: { title: "Denied title" },
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
).toEqual({
|
|
||||||
status: "error",
|
|
||||||
error: { type: "permission.rejected", message: "Permission denied: sessionRename" },
|
|
||||||
})
|
|
||||||
expect(renamed).toEqual([])
|
|
||||||
deny = false
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue