/**
 * ============================================================
 *  src/workflows/stepExecutors/tool.ts —— tool step 执行
 * ------------------------------------------------------------
 *  把 step.args 递归 ${var} 插值后 JSON.stringify，转发到 executeTool。
 *  失败（ok=false）抛 Error，让 runner 走 onError 处理。
 *
 *  v2（P1 TUI 可见）：本执行器改为 AsyncGenerator，在调 executeTool 前后
 *  yield 通用 tool_start / tool_end LoopEvent —— 这样 workflow 的 tool 步骤
 *  在 TUI 里像普通 ReAct 工具调用一样显示（runningTools spinner + 人话描述），
 *  不再"无声"。**不** yield tool_messages_committed：workflow 步骤不进主 history，
 *  其 live 态由 useChat 的 submit finally(resetTurnState) 在事件流末尾统一清理；
 *  tool_end 自身已把该 callId 从 runningTools 里摘除，无需额外 commit 语义。
 *
 *  capture 字段（支持点路径）：
 *    - `{ content: var_name }`      绑 ToolResult.content（工具回执文本）
 *    - `{ result: var_name }`       同义，给"无结构化字段"工具用
 *    - `{ args.content: var_name }` 绑插值后传给工具的入参（如真正写入的内容）
 *    - `{ args.<任意字段>: var_name }` 绑入参的具名字段
 * ============================================================
 */
import { executeTool, getToolByName, } from '../../../../src/plugin-sdk.js';
import { interpolateDeep } from '../expressions.js';
export async function* execToolStep(step, vars, ctx) {
    if (!step.tool)
        throw new Error(`step ${step.id}: 缺少 tool 字段`);
    const tool = getToolByName(step.tool);
    if (!tool)
        throw new Error(`step ${step.id}: 未知 tool "${step.tool}"`);
    const args = interpolateDeep(step.args ?? {}, vars);
    const argsJson = JSON.stringify(args);
    const toolCallId = `wf_${step.id}`;
    const argsPreview = argsJson.length > 80 ? `${argsJson.slice(0, 80)}…` : argsJson;
    yield {
        type: 'tool_start',
        toolName: tool.name,
        toolCallId,
        argsPreview,
        argsFriendly: friendlyToolDesc(tool.name, args),
    };
    const result = await executeTool(tool.name, argsJson, ctx.signal);
    yield {
        type: 'tool_end',
        toolName: tool.name,
        toolCallId,
        ok: result.ok,
        content: result.ok ? result.content : result.error,
    };
    if (!result.ok) {
        throw new Error(result.error);
    }
    const content = result.content;
    return {
        raw: { content, result: content, args },
        preview: content.length > 200 ? `${content.slice(0, 200)}...` : content,
    };
}
/**
 * 给 tool_start 生成人话描述（LiveArea spinner 行用）。
 * 取常见入参键（file_path/command/query/...）拼成 "Write greetings/a/hello.txt"
 * 这样的短句；都没命中就回退到工具名本身。
 */
function friendlyToolDesc(toolName, args) {
    const key = ['file_path', 'command', 'query', 'pattern', 'path', 'url'].find((k) => typeof args[k] === 'string');
    if (!key)
        return toolName;
    const val = String(args[key]);
    const short = val.length > 60 ? `${val.slice(0, 60)}…` : val;
    return `${toolName} ${short}`;
}
