import type { Tool } from './index' import type { SandboxWorkspaceAction } from '@shared/sandbox' import { workspaceAction } from '../../sandbox/workspace-service' export const sandboxWorkspaceTool: Tool = { name: 'sandbox_workspace', description: '管理当前会话的长期沙盒草稿。status 查询已保存状态;changes 同步宿主未冲突的外部编辑并查看差异;commit 经批准写入宿主项目;discard 归档草稿并重新读取宿主;recover 恢复中断或核对宿主结果。用 execute_command 的 workspace=persistent 读写草稿,普通文件工具仍读写宿主。默认私有 Git 不写宿主 .git;若已用 sandbox_git 导入真实仓库或创建/克隆新仓库,commit/discard/recover 会统一处理文件与 Git 元数据。不接受其他会话或任意目录。', parameters: { type: 'object', properties: { action: { type: 'string', enum: ['status', 'changes', 'commit', 'discard', 'recover'] }, expectedRevision: { type: 'string', description: '可选的已观察版本;版本变化时拒绝操作。' } }, required: ['action'], additionalProperties: false }, async run(args, ctx) { if (Object.keys(args).some(key => key !== 'action' && key !== 'expectedRevision')) throw new Error('长期工作区不接受路径或其他调用归属参数') if (typeof args.action !== 'string' || !['status', 'changes', 'commit', 'discard', 'recover'].includes(args.action) || args.expectedRevision !== undefined && typeof args.expectedRevision !== 'string') throw new Error('长期工作区参数无效') if (!ctx.sessionId) throw new Error('长期工作区必须由宿主会话调用') const data = await workspaceAction(args.action as SandboxWorkspaceAction, { ...ctx, sessionId: ctx.sessionId }, args.expectedRevision as string | undefined) const ok = !data.reason && !['conflict', 'stale', 'interrupted', 'invalid'].includes(data.state) && data.writeback !== 'rejected' const content = [`长期工作区:${data.state};${data.changes.length} 项待提交变更。`, data.writeback === 'committed' ? data.git ? '已按审批一起保存项目文件与真实宿主 Git 元数据。' : '已按审批保存到宿主项目;私有 Git 不写宿主 .git。' : '此草稿尚未自动写入宿主项目。', ...data.changes.slice(0, 50).map(change => `${change.operation}: ${change.path}`), data.reason ?? ''].filter(Boolean).join('\n') return { ok, content, data } } }