(params: EditContentArgs, context?: ServerToolContext)
| 27 | export const editContentServerTool: BaseServerTool<EditContentArgs, EditContentResult> = { |
| 28 | name: 'edit_content', |
| 29 | async execute(params: EditContentArgs, context?: ServerToolContext): Promise<EditContentResult> { |
| 30 | if (!context?.userId) { |
| 31 | logger.error('Unauthorized attempt to use edit_content') |
| 32 | throw new Error('Authentication required') |
| 33 | } |
| 34 | |
| 35 | const workspaceId = context.workspaceId |
| 36 | if (!workspaceId) { |
| 37 | return { success: false, message: 'Workspace ID is required' } |
| 38 | } |
| 39 | |
| 40 | const raw = params as Record<string, unknown> |
| 41 | const nested = raw.args as Record<string, unknown> | undefined |
| 42 | const content = |
| 43 | typeof params.content === 'string' |
| 44 | ? params.content |
| 45 | : typeof nested?.content === 'string' |
| 46 | ? (nested.content as string) |
| 47 | : undefined |
| 48 | |
| 49 | if (content === undefined) { |
| 50 | return { success: false, message: 'content is required for edit_content' } |
| 51 | } |
| 52 | |
| 53 | // Consume the intent from THIS file subagent's channel (its outer tool_use |
| 54 | // id), not just the latest in the message — otherwise two file agents |
| 55 | // writing concurrently would each grab whichever workspace_file landed last |
| 56 | // and write their content into the wrong file. Falls back to latest-in- |
| 57 | // message when no channel id is present (main-agent / legacy calls). |
| 58 | const intent = await consumeLatestFileIntent(workspaceId, { |
| 59 | chatId: context.chatId, |
| 60 | messageId: context.messageId, |
| 61 | channelId: context.parentToolCallId, |
| 62 | }) |
| 63 | if (!intent) { |
| 64 | return { |
| 65 | success: false, |
| 66 | message: |
| 67 | 'No workspace_file context found. Call workspace_file first, wait for it to succeed, then call edit_content in the next step. Do not emit edit_content in parallel or in the same batch as workspace_file.', |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | try { |
| 72 | const { operation, fileRecord } = intent |
| 73 | const docInfo = getDocumentFormatInfo(fileRecord.name) |
| 74 | const e2bFmt = isE2BDocEnabled ? await getE2BDocFormat(fileRecord.name) : null |
| 75 | |
| 76 | let finalContent: string |
| 77 | switch (operation) { |
| 78 | case 'append': { |
| 79 | const existing = intent.existingContent ?? '' |
| 80 | // The JS engines (isolated-vm and E2B-node pptx/docx) use the `{ ... }` |
| 81 | // block-append convention — block statements scope cleanly inside the |
| 82 | // compile wrapper. Python docs (pdf/xlsx) are a single cohesive script, |
| 83 | // so brace-wrapping would produce invalid Python; plain-concatenate. |
| 84 | // Brace-wrap appended content for the JS engines (isolated-vm and |
| 85 | // E2B-node pptx/docx); Python docs (pdf/xlsx) are one cohesive script. |
| 86 | const braceWrap = e2bFmt ? e2bFmt.engine === 'node' : docInfo.isDoc |
nothing calls this directly
no test coverage detected