/** * v0.4.0 冒烟:业务工具注册 / 手动直调(D2 双模式路径)/ tool_call 拦截契约 * 说明:审批拦截逻辑与 src/main/pi/approval.ts 保持一致(此处内联同构实现, * 因冒烟脚本无法直接加载 TS 源文件;拦截契约由 pi 的 ExtensionHandler 类型保证)。 */ import { createAgentSessionFromServices, createAgentSessionServices, defineTool, getAgentDir, SessionManager } from '@earendil-works/pi-coding-agent' import { Type } from '@earendil-works/pi-ai' import { homedir } from 'node:os' const cwd = homedir() const agentDir = getAgentDir() // 与 src/main/tools/demo-tools.ts 同构的工具定义 const hotwordLookup = defineTool({ name: 'hotword_lookup', label: '热点词查询(演示)', description: 'demo', parameters: Type.Object({ query: Type.String(), limit: Type.Optional(Type.Number()) }), async execute(_id, params) { return { content: [{ type: 'text', text: `热点词查询结果(演示数据源,query="${params.query}"):ok` }], details: { source: 'demo' } } } }) // ---- 1) 工具注册检查(customTools 路径) ---- const services = await createAgentSessionServices({ cwd, agentDir }) const { session } = await createAgentSessionFromServices({ services, sessionManager: SessionManager.inMemory(cwd), customTools: [hotwordLookup] }) const all = session.getAllTools() console.log('[smoke4] registered tools:', all.map((t) => t.name).join(', ')) console.log('[smoke4] hotword_lookup registered:', all.some((t) => t.name === 'hotword_lookup')) // ---- 2) 手动直调(双模式共用能力的证明,不经 LLM) ---- const result = await hotwordLookup.execute('smoke-call', { query: '宠物' }, undefined, undefined, {}) console.log('[smoke4] manual execute text:', JSON.stringify(result.content[0]?.text?.slice(0, 60))) // ---- 3) tool_call 拦截契约(模拟 ExtensionAPI 捕获 handler) ---- const handlers = {} const fakePi = { on: (event, handler) => { handlers[event] = handler } } const needsApproval = new Set(['price_check', 'bash']) let decision = false fakePi.on('tool_call', async (event) => { if (!needsApproval.has(event.toolName)) return const approved = decision if (!approved) return { block: true, reason: '用户拒绝了本次工具调用审批' } return }) const passthrough = await handlers['tool_call']({ type: 'tool_call', toolName: 'read', input: { path: 'x' } }) console.log('[smoke4] read(不在名单)放行:', passthrough === undefined) decision = false const denied = await handlers['tool_call']({ type: 'tool_call', toolName: 'price_check', input: { product_id: 'P1', target_price: 9.9 } }) console.log('[smoke4] price_check 拒绝 -> block:', JSON.stringify(denied)) decision = true const approvedRes = await handlers['tool_call']({ type: 'tool_call', toolName: 'bash', input: { command: 'ls' } }) console.log('[smoke4] bash 批准 -> 放行:', approvedRes === undefined) console.log('[smoke4] done') process.exit(0)