refactor(cli): convert debug agent command to effectCmd (#25485)

This commit is contained in:
Kit Langton
2026-05-02 19:19:06 -04:00
committed by GitHub
parent 3f459819ba
commit 7ab1c1c74a
+90 -101
View File
@@ -7,14 +7,14 @@ import { Session } from "@/session/session"
import type { MessageV2 } from "../../../session/message-v2" import type { MessageV2 } from "../../../session/message-v2"
import { MessageID, PartID } from "../../../session/schema" import { MessageID, PartID } from "../../../session/schema"
import { ToolRegistry } from "@/tool/registry" import { ToolRegistry } from "@/tool/registry"
import { Instance } from "../../../project/instance"
import { Permission } from "../../../permission" import { Permission } from "../../../permission"
import { iife } from "../../../util/iife" import { iife } from "../../../util/iife"
import { bootstrap } from "../../bootstrap" import { effectCmd, fail } from "../../effect-cmd"
import { cmd } from "../cmd" import { InstanceRef } from "@/effect/instance-ref"
import { AppRuntime } from "@/effect/app-runtime" import { InstanceStore } from "@/project/instance-store"
import type { InstanceContext } from "@/project/instance"
export const AgentCommand = cmd({ export const AgentCommand = effectCmd({
command: "agent <name>", command: "agent <name>",
describe: "show agent configuration details", describe: "show agent configuration details",
builder: (yargs) => builder: (yargs) =>
@@ -32,60 +32,61 @@ export const AgentCommand = cmd({
type: "string", type: "string",
description: "Tool params as JSON or a JS object literal", description: "Tool params as JSON or a JS object literal",
}), }),
async handler(args) { handler: Effect.fn("Cli.debug.agent")(function* (args) {
await bootstrap(process.cwd(), async () => { const ctx = yield* InstanceRef
const agentName = args.name as string if (!ctx) return
const agent = await AppRuntime.runPromise(Agent.Service.use((svc) => svc.get(agentName))) const store = yield* InstanceStore.Service
if (!agent) { return yield* run(args, ctx).pipe(Effect.ensuring(store.dispose(ctx)))
process.stderr.write( }),
`Agent ${agentName} not found, run '${basename(process.execPath)} agent list' to get an agent list` + EOL,
)
process.exit(1)
}
const availableTools = await getAvailableTools(agent)
const resolvedTools = await resolveTools(agent, availableTools)
const toolID = args.tool as string | undefined
if (toolID) {
const tool = availableTools.find((item) => item.id === toolID)
if (!tool) {
process.stderr.write(`Tool ${toolID} not found for agent ${agentName}` + EOL)
process.exit(1)
}
if (resolvedTools[toolID] === false) {
process.stderr.write(`Tool ${toolID} is disabled for agent ${agentName}` + EOL)
process.exit(1)
}
const params = parseToolParams(args.params as string | undefined)
const ctx = await createToolContext(agent)
const result = await tool.execute(params, ctx)
process.stdout.write(JSON.stringify({ tool: toolID, input: params, result }, null, 2) + EOL)
return
}
const output = {
...agent,
tools: resolvedTools,
}
process.stdout.write(JSON.stringify(output, null, 2) + EOL)
})
},
}) })
async function getAvailableTools(agent: Agent.Info) { const run = Effect.fn("Cli.debug.agent.body")(function* (
return AppRuntime.runPromise( args: { name: string; tool?: string; params?: string },
Effect.gen(function* () { ctx: InstanceContext,
const provider = yield* Provider.Service ) {
const registry = yield* ToolRegistry.Service const agentName = args.name
const model = agent.model ?? (yield* provider.defaultModel()) const agent = yield* Agent.Service.use((svc) => svc.get(agentName))
return yield* registry.tools({ if (!agent) {
...model, process.stderr.write(
agent, `Agent ${agentName} not found, run '${basename(process.execPath)} agent list' to get an agent list` + EOL,
}) )
}), return yield* fail("", 1)
) }
} const availableTools = yield* getAvailableTools(agent)
const resolvedTools = resolveTools(agent, availableTools)
const toolID = args.tool
if (toolID) {
const tool = availableTools.find((item) => item.id === toolID)
if (!tool) {
process.stderr.write(`Tool ${toolID} not found for agent ${agentName}` + EOL)
return yield* fail("", 1)
}
if (resolvedTools[toolID] === false) {
process.stderr.write(`Tool ${toolID} is disabled for agent ${agentName}` + EOL)
return yield* fail("", 1)
}
const params = parseToolParams(args.params)
const toolCtx = yield* createToolContext(agent, ctx)
const result = yield* tool.execute(params, toolCtx)
process.stdout.write(JSON.stringify({ tool: toolID, input: params, result }, null, 2) + EOL)
return
}
async function resolveTools(agent: Agent.Info, availableTools: Awaited<ReturnType<typeof getAvailableTools>>) { const output = {
...agent,
tools: resolvedTools,
}
process.stdout.write(JSON.stringify(output, null, 2) + EOL)
})
const getAvailableTools = Effect.fn("Cli.debug.agent.getAvailableTools")(function* (agent: Agent.Info) {
const provider = yield* Provider.Service
const registry = yield* ToolRegistry.Service
const model = agent.model ?? (yield* provider.defaultModel())
return yield* registry.tools({ ...model, agent })
})
function resolveTools(agent: Agent.Info, availableTools: { id: string }[]) {
const disabled = Permission.disabled( const disabled = Permission.disabled(
availableTools.map((tool) => tool.id), availableTools.map((tool) => tool.id),
agent.permission, agent.permission,
@@ -123,50 +124,38 @@ function parseToolParams(input?: string) {
return parsed as Record<string, unknown> return parsed as Record<string, unknown>
} }
async function createToolContext(agent: Agent.Info) { const createToolContext = Effect.fn("Cli.debug.agent.createToolContext")(function* (
const { session, messageID } = await AppRuntime.runPromise( agent: Agent.Info,
Effect.gen(function* () { ctx: InstanceContext,
const session = yield* Session.Service ) {
const result = yield* session.create({ title: `Debug tool run (${agent.name})` }) const sessionSvc = yield* Session.Service
const messageID = MessageID.ascending() const session = yield* sessionSvc.create({ title: `Debug tool run (${agent.name})` })
const model = agent.model const messageID = MessageID.ascending()
? agent.model const model = agent.model
: yield* Effect.gen(function* () { ? agent.model
const provider = yield* Provider.Service : yield* Effect.gen(function* () {
return yield* provider.defaultModel() const provider = yield* Provider.Service
}) return yield* provider.defaultModel()
const now = Date.now() })
const message: MessageV2.Assistant = { const now = Date.now()
id: messageID, const message: MessageV2.Assistant = {
sessionID: result.id, id: messageID,
role: "assistant", sessionID: session.id,
time: { role: "assistant",
created: now, time: { created: now },
}, parentID: messageID,
parentID: messageID, modelID: model.modelID,
modelID: model.modelID, providerID: model.providerID,
providerID: model.providerID, mode: "debug",
mode: "debug", agent: agent.name,
agent: agent.name, path: {
path: { cwd: ctx.directory,
cwd: Instance.directory, root: ctx.worktree,
root: Instance.worktree, },
}, cost: 0,
cost: 0, tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
tokens: { }
input: 0, yield* sessionSvc.updateMessage(message)
output: 0,
reasoning: 0,
cache: {
read: 0,
write: 0,
},
},
}
yield* session.updateMessage(message)
return { session: result, messageID }
}),
)
const ruleset = Permission.merge(agent.permission, session.permission ?? []) const ruleset = Permission.merge(agent.permission, session.permission ?? [])
@@ -189,4 +178,4 @@ async function createToolContext(agent: Agent.Info) {
}) })
}, },
} }
} })