/
Alex_Ural
/
opencode
Обзор
Документация
Войти
/
Alex_Ural
/
opencode
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
packages/core/src/tool/question.ts
94 строки
3 KB
James Long
refactor(core): remove tool layer exports (#34622)
30 июн 2026, 18:18
Не верифицирован
30 июн 2026, 18:18
5691f36
Код
Авторство
О чём код?
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" export const name = "question" export const description = `Use this tool when you need to ask the user questions during execution. This allows you to: 1. Gather user preferences or requirements 2. Clarify ambiguous instructions 3. Get decisions on implementation choices as you work 4. Offer choices to the user about what direction to take. Usage notes: - When \`custom\` is enabled (default), a "Type your own answer" option is added automatically; don't include "Other" or catch-all options - Answers are returned as arrays of labels; set \`multiple: true\` to allow selecting more than one - If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label` export const Input = Schema.Struct({ questions: Schema.Array(QuestionV2.Prompt).annotate({ description: "Questions to ask" }), }) export const Output = Schema.Struct({ answers: Schema.Array(QuestionV2.Answer), }) export type Output = typeof Output.Type export const toModelOutput = ( questions: ReadonlyArray<QuestionV2.Prompt>, answers: ReadonlyArray<QuestionV2.Answer>, ) => { const formatted = questions .map( (question, index) => `"${question.question}"="${answers[index]?.length ? answers[index].join(", ") : "Unanswered"}"`, ) .join(", ") return `User has answered your questions: ${formatted}. You can now continue with the user's answers in mind.` } const layer = Layer.effectDiscard( Effect.gen(function* () { const tools = yield* Tools.Service const question = yield* QuestionV2.Service const permission = yield* PermissionV2.Service yield* tools .register({ [name]: Tool.make({ description, input: Input, output: Output, toModelOutput: ({ input, output }) => [ { type: "text", text: toModelOutput(input.questions, output.answers) }, ], execute: (input, context) => permission .assert({ action: "question", resources: ["*"], sessionID: context.sessionID, agent: context.agent, source: { type: "tool", messageID: context.assistantMessageID, callID: context.toolCallID }, }) .pipe( Effect.mapError(() => new ToolFailure({ message: "Permission denied: question" })), Effect.andThen( question .ask({ sessionID: context.sessionID, questions: input.questions, tool: { messageID: context.assistantMessageID, callID: context.toolCallID }, }) .pipe(Effect.orDie), ), Effect.map((answers) => ({ answers })), ), }), }) .pipe(Effect.orDie) }), ) export const node = makeLocationNode({ name: "tool/question", layer, deps: [ToolRegistry.node, PermissionV2.node, QuestionV2.node], })