From 3908560cdd3312c3405f091b9be0b09fa0318318 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 02:04:43 +0000 Subject: [PATCH 1/6] Initial plan From 4e56dc216b4b506ec1c6e367357078cfd819be5c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 02:12:30 +0000 Subject: [PATCH 2/6] Initial exploration - understanding the safe_outputs_mcp_server structure Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/artifacts-summary.lock.yml | 152 +++++++++--------- .github/workflows/brave.lock.yml | 152 +++++++++--------- .github/workflows/ci-doctor.lock.yml | 152 +++++++++--------- .github/workflows/daily-news.lock.yml | 152 +++++++++--------- .github/workflows/dev-hawk.lock.yml | 152 +++++++++--------- .github/workflows/dev.lock.yml | 152 +++++++++--------- .github/workflows/mcp-inspector.lock.yml | 152 +++++++++--------- .../workflows/notion-issue-summary.lock.yml | 152 +++++++++--------- .github/workflows/pdf-summary.lock.yml | 152 +++++++++--------- .github/workflows/plan.lock.yml | 152 +++++++++--------- .github/workflows/poem-bot.lock.yml | 152 +++++++++--------- .github/workflows/q.lock.yml | 152 +++++++++--------- .github/workflows/repo-tree-map.lock.yml | 152 +++++++++--------- .github/workflows/research.lock.yml | 152 +++++++++--------- .github/workflows/scout.lock.yml | 152 +++++++++--------- .github/workflows/smoke-copilot.lock.yml | 152 +++++++++--------- .github/workflows/test-jqschema.lock.yml | 152 +++++++++--------- .github/workflows/test-post-steps.lock.yml | 152 +++++++++--------- .github/workflows/test-svelte.lock.yml | 152 +++++++++--------- .github/workflows/tidy.lock.yml | 152 +++++++++--------- .github/workflows/video-analyzer.lock.yml | 152 +++++++++--------- 21 files changed, 1596 insertions(+), 1596 deletions(-) diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index 625b3695bb2..5688155fd2d 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -2487,96 +2487,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index eaaf2d246d3..e96eec085c6 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -3601,96 +3601,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index daa46585e34..063adb65a00 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -3022,96 +3022,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index a4063df5737..c07ecb12fb2 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -2665,96 +2665,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index 2c0badd1c61..07d2e8a8aa9 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -2922,96 +2922,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 50fae58af96..1b26e4b4983 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -943,96 +943,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index bfa03ea9c85..7954b44b13e 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -3443,96 +3443,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index b4a90136958..a573d82a8f7 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -2459,96 +2459,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 0b47eecb131..edded251635 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -3551,96 +3551,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index c4e7f5c4cd9..85e135a6d47 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -3067,96 +3067,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 0429a8cac76..bb8e8dc2089 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -3795,96 +3795,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 8b109d1bbbd..703139c0fb7 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -3854,96 +3854,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index f321bb8b7ff..658418700b1 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -2554,96 +2554,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index 8636139c2ab..a5e2dca0632 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -2477,96 +2477,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 31168304189..56b254e5cc3 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -4067,96 +4067,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index e592f302e44..ba2d18a0c9d 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -2427,96 +2427,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/test-jqschema.lock.yml b/.github/workflows/test-jqschema.lock.yml index 3e76318cc50..dfd7c6bb1dc 100644 --- a/.github/workflows/test-jqschema.lock.yml +++ b/.github/workflows/test-jqschema.lock.yml @@ -1017,96 +1017,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/test-post-steps.lock.yml b/.github/workflows/test-post-steps.lock.yml index df85491cd40..552ce93c8ee 100644 --- a/.github/workflows/test-post-steps.lock.yml +++ b/.github/workflows/test-post-steps.lock.yml @@ -914,96 +914,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/test-svelte.lock.yml b/.github/workflows/test-svelte.lock.yml index 858d7a361c8..c04ec7ec658 100644 --- a/.github/workflows/test-svelte.lock.yml +++ b/.github/workflows/test-svelte.lock.yml @@ -952,96 +952,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 727b3cdcb0b..89297d01175 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -2952,96 +2952,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index bab3df07f8c..d266c1a7672 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -2722,96 +2722,96 @@ jobs: try { const jsonStr = currentJsonLines.join("\n"); const jsonData = JSON.parse(jsonStr); - if (jsonData.model) { - model = jsonData.model; - } - if (jsonData.choices && Array.isArray(jsonData.choices)) { - for (const choice of jsonData.choices) { - if (choice.message) { - const message = choice.message; - const content = []; - const toolResults = []; - if (message.content && message.content.trim()) { - content.push({ - type: "text", - text: message.content, - }); - } - if (message.tool_calls && Array.isArray(message.tool_calls)) { - for (const toolCall of message.tool_calls) { - if (toolCall.function) { - let toolName = toolCall.function.name; - let args = {}; - if (toolName.startsWith("github-")) { - toolName = "mcp__github__" + toolName.substring(7); - } else if (toolName === "bash") { - toolName = "Bash"; - } - try { - args = JSON.parse(toolCall.function.arguments); - } catch (e) { - args = {}; + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); } - const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; - content.push({ - type: "tool_use", - id: toolId, - name: toolName, - input: args, - }); - toolResults.push({ - type: "tool_result", - tool_use_id: toolId, - content: "", - is_error: false, - }); } } - } - if (content.length > 0) { - entries.push({ - type: "assistant", - message: { content }, - }); - turnCount++; - if (toolResults.length > 0) { + if (content.length > 0) { entries.push({ - type: "user", - message: { content: toolResults }, + type: "assistant", + message: { content }, }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } } } } - } - if (jsonData.usage) { - if (!entries._accumulatedUsage) { - entries._accumulatedUsage = { - input_tokens: 0, - output_tokens: 0, + if (jsonData.usage) { + if (!entries._accumulatedUsage) { + entries._accumulatedUsage = { + input_tokens: 0, + output_tokens: 0, + }; + } + if (jsonData.usage.prompt_tokens) { + entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; + } + if (jsonData.usage.completion_tokens) { + entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; + } + entries._lastResult = { + type: "result", + num_turns: turnCount, + usage: entries._accumulatedUsage, }; } - if (jsonData.usage.prompt_tokens) { - entries._accumulatedUsage.input_tokens += jsonData.usage.prompt_tokens; - } - if (jsonData.usage.completion_tokens) { - entries._accumulatedUsage.output_tokens += jsonData.usage.completion_tokens; - } - entries._lastResult = { - type: "result", - num_turns: turnCount, - usage: entries._accumulatedUsage, - }; } + } catch (e) { } - } catch (e) { } + inDataBlock = false; + currentJsonLines = []; + continue; + } else if (hasTimestamp && isJsonContent) { + currentJsonLines.push(cleanLine); } - inDataBlock = false; - currentJsonLines = []; - continue; - } else if (hasTimestamp && isJsonContent) { - currentJsonLines.push(cleanLine); - } } else { const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); currentJsonLines.push(cleanLine); From 3648e5c936a0a2264ea2524121b6a75b0b59e37f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 02:17:16 +0000 Subject: [PATCH 3/6] Add large content handling to safe_outputs_mcp_server - Implement token counting using 4 chars = 1 token estimate - Detect content exceeding 16000 tokens in tool outputs - Write large content to files in /tmp/gh-aw/safe-outputs/ with SHA256 hash filenames - Automatically detect file extension (.json, .md, .txt) based on content - Return JSON format: { filename: "hash.ext", description: "generated content large!" } - Replace large field in safe output with file reference - Add comprehensive test coverage for all scenarios Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/artifacts-summary.lock.yml | 62 +++ .github/workflows/audit-workflows.lock.yml | 62 +++ .github/workflows/brave.lock.yml | 62 +++ .../workflows/changeset-generator.lock.yml | 62 +++ .github/workflows/ci-doctor.lock.yml | 62 +++ .../workflows/cli-version-checker.lock.yml | 62 +++ .../workflows/copilot-agent-analysis.lock.yml | 62 +++ .github/workflows/daily-doc-updater.lock.yml | 62 +++ .github/workflows/daily-news.lock.yml | 62 +++ .github/workflows/dev-hawk.lock.yml | 62 +++ .../duplicate-code-detector.lock.yml | 62 +++ .../example-workflow-analyzer.lock.yml | 62 +++ .../github-mcp-tools-report.lock.yml | 62 +++ .../workflows/go-pattern-detector.lock.yml | 62 +++ .github/workflows/issue-classifier.lock.yml | 62 +++ .github/workflows/lockfile-stats.lock.yml | 62 +++ .github/workflows/mcp-inspector.lock.yml | 62 +++ .../workflows/notion-issue-summary.lock.yml | 62 +++ .github/workflows/pdf-summary.lock.yml | 62 +++ .github/workflows/plan.lock.yml | 62 +++ .github/workflows/poem-bot.lock.yml | 62 +++ .github/workflows/q.lock.yml | 62 +++ .github/workflows/repo-tree-map.lock.yml | 62 +++ .github/workflows/research.lock.yml | 62 +++ .github/workflows/scout.lock.yml | 62 +++ .github/workflows/security-fix-pr.lock.yml | 62 +++ .github/workflows/smoke-claude.lock.yml | 62 +++ .github/workflows/smoke-codex.lock.yml | 62 +++ .github/workflows/smoke-copilot.lock.yml | 62 +++ .github/workflows/smoke-genaiscript.lock.yml | 62 +++ .github/workflows/smoke-opencode.lock.yml | 62 +++ .../workflows/technical-doc-writer.lock.yml | 62 +++ .github/workflows/tidy.lock.yml | 62 +++ .github/workflows/unbloat-docs.lock.yml | 62 +++ .github/workflows/video-analyzer.lock.yml | 62 +++ .../safe_outputs_mcp_large_content.test.cjs | 519 ++++++++++++++++++ pkg/workflow/js/safe_outputs_mcp_server.cjs | 113 ++++ 37 files changed, 2802 insertions(+) create mode 100644 pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index 5688155fd2d..c03ff30565e 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -282,6 +282,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -294,6 +329,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index ea622d7b5a9..4bb78966ecd 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -432,6 +432,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -444,6 +479,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index e96eec085c6..1bf626fc083 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1290,6 +1290,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1302,6 +1337,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/changeset-generator.lock.yml b/.github/workflows/changeset-generator.lock.yml index 54bb3605619..783a02bc2fb 100644 --- a/.github/workflows/changeset-generator.lock.yml +++ b/.github/workflows/changeset-generator.lock.yml @@ -905,6 +905,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -917,6 +952,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 063adb65a00..cff2d413ea8 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -682,6 +682,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -694,6 +729,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index b52adbdeb9b..c3ef9f88608 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -398,6 +398,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -410,6 +445,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index f35c68e5cc0..8d7d47cf72e 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -421,6 +421,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -433,6 +468,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index bfa8d1b1342..7ad42f8b514 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -412,6 +412,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -424,6 +459,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index c07ecb12fb2..5cfdf0fc0b8 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -309,6 +309,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -321,6 +356,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index 07d2e8a8aa9..fa05b7d5376 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -659,6 +659,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -671,6 +706,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 211a339e2cb..b374c1e4e5a 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -309,6 +309,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -321,6 +356,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index 19538b75220..8dfb965fc67 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -398,6 +398,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -410,6 +445,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index dd9a04ec30c..bfedca7c45d 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -410,6 +410,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -422,6 +457,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 3809bf46d70..59587ca5439 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -397,6 +397,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -409,6 +444,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index dce4400ddc3..5016fdb2c89 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -1010,6 +1010,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1022,6 +1057,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index b949851ba0f..315fa936922 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -411,6 +411,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -423,6 +458,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index 7954b44b13e..9fe0821787d 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -735,6 +735,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -747,6 +782,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index a573d82a8f7..b7d244b5a91 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -282,6 +282,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -294,6 +329,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index edded251635..e8bbbf140f3 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1203,6 +1203,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1215,6 +1250,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index 85e135a6d47..110c13ffe3d 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -785,6 +785,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -797,6 +832,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index bb8e8dc2089..887f32a5cbe 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1465,6 +1465,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1477,6 +1512,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 703139c0fb7..e31f642c2e8 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1242,6 +1242,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1254,6 +1289,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index 658418700b1..58c62208cf2 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -280,6 +280,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -292,6 +327,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index a5e2dca0632..0bdb31b5eb8 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -289,6 +289,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -301,6 +336,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 56b254e5cc3..f55b6eef6a1 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1481,6 +1481,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1493,6 +1528,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index 504e3ef9922..76f0a61c72a 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -410,6 +410,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -422,6 +457,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 7dc598483e1..0b71570bdb7 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -387,6 +387,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -399,6 +434,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 14cd7790d29..98710769946 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -283,6 +283,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -295,6 +330,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index ba2d18a0c9d..b48e94f65be 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -278,6 +278,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -290,6 +325,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/smoke-genaiscript.lock.yml b/.github/workflows/smoke-genaiscript.lock.yml index c50ce38ef6f..3db944a9ecb 100644 --- a/.github/workflows/smoke-genaiscript.lock.yml +++ b/.github/workflows/smoke-genaiscript.lock.yml @@ -268,6 +268,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -280,6 +315,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml index 8faf3f0f96f..a4e2715b8ae 100644 --- a/.github/workflows/smoke-opencode.lock.yml +++ b/.github/workflows/smoke-opencode.lock.yml @@ -268,6 +268,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -280,6 +315,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 23106a3012d..79aab5d8c69 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -810,6 +810,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -822,6 +857,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 89297d01175..c7695d7a76f 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -648,6 +648,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -660,6 +695,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index c25f22bbed6..3c6c4ce9c49 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1154,6 +1154,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -1166,6 +1201,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index d266c1a7672..9ee7c55550d 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -297,6 +297,41 @@ jobs: }; writeMessage(res); } + function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); + } + function getFileExtension(content) { + if (!content) return ".txt"; + const trimmed = content.trim(); + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + } + } + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + return ".txt"; + } + function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + const hash = crypto.createHash("sha256").update(content).digest("hex"); + const ext = getFileExtension(content); + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + fs.writeFileSync(filepath, content, "utf8"); + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + return { + filename: filename, + description: "generated content large!" + }; + } function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); entry.type = entry.type.replace(/-/g, "_"); @@ -309,6 +344,33 @@ jobs: } const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + if (largeContent && largeFieldName) { + const fileInfo = writeLargeContentToFile(largeContent); + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + appendSafeOutput(entry); + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } appendSafeOutput(entry); return { content: [ diff --git a/pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs b/pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs new file mode 100644 index 00000000000..55d49710277 --- /dev/null +++ b/pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs @@ -0,0 +1,519 @@ +import { describe, it, expect, beforeEach, afterEach } from "vitest"; +import fs from "fs"; +import path from "path"; +import { spawn } from "child_process"; +import crypto from "crypto"; + +describe("safe_outputs_mcp_server.cjs large content handling", () => { + let originalEnv; + let tempOutputDir; + let tempConfigFile; + let tempOutputFile; + + beforeEach(() => { + originalEnv = { ...process.env }; + + // Create temporary directories for testing + tempOutputDir = path.join("/tmp", `test_large_content_${Date.now()}`); + fs.mkdirSync(tempOutputDir, { recursive: true }); + + tempConfigFile = path.join(tempOutputDir, "config.json"); + tempOutputFile = path.join(tempOutputDir, "outputs.jsonl"); + + // Create a simple config with create_issue enabled + const config = { + "create-issue": {}, + }; + fs.writeFileSync(tempConfigFile, JSON.stringify(config)); + }); + + afterEach(() => { + process.env = originalEnv; + + // Clean up temporary files + if (fs.existsSync(tempOutputDir)) { + fs.rmSync(tempOutputDir, { recursive: true, force: true }); + } + }); + + it("should write large content to file when exceeding 16000 tokens", async () => { + // Set up environment + process.env.GH_AW_SAFE_OUTPUTS = tempOutputFile; + process.env.GH_AW_SAFE_OUTPUTS_CONFIG = fs.readFileSync(tempConfigFile, "utf8"); + + const serverPath = path.join(__dirname, "safe_outputs_mcp_server.cjs"); + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + child.kill(); + reject(new Error("Test timeout")); + }, 10000); + + const child = spawn("node", [serverPath], { + stdio: ["pipe", "pipe", "pipe"], + env: { ...process.env }, + }); + + let stderr = ""; + let stdout = ""; + + child.stderr.on("data", data => { + stderr += data.toString(); + }); + + child.stdout.on("data", data => { + stdout += data.toString(); + }); + + child.on("error", error => { + clearTimeout(timeout); + reject(error); + }); + + // Send initialization + const initMessage = + JSON.stringify({ + jsonrpc: "2.0", + id: 1, + method: "initialize", + params: { + protocolVersion: "2024-11-05", + capabilities: {}, + clientInfo: { name: "test-client", version: "1.0.0" }, + }, + }) + "\n"; + + child.stdin.write(initMessage); + + // Wait for initialization, then send large content + setTimeout(() => { + // Create content that's larger than 16000 tokens (> 64000 characters) + const largeBody = "A".repeat(70000); // ~17500 tokens + + const toolCallMessage = + JSON.stringify({ + jsonrpc: "2.0", + id: 2, + method: "tools/call", + params: { + name: "create_issue", + arguments: { + title: "Test Issue", + body: largeBody, + }, + }, + }) + "\n"; + + child.stdin.write(toolCallMessage); + + // Wait for response + setTimeout(() => { + child.kill(); + clearTimeout(timeout); + + // Parse the response + const responses = stdout.trim().split("\n"); + const toolCallResponse = responses.find(line => { + try { + const parsed = JSON.parse(line); + return parsed.id === 2; + } catch { + return false; + } + }); + + expect(toolCallResponse).toBeDefined(); + + const parsed = JSON.parse(toolCallResponse); + expect(parsed.result).toBeDefined(); + expect(parsed.result.content).toBeDefined(); + expect(parsed.result.content.length).toBeGreaterThan(0); + + const responseText = parsed.result.content[0].text; + const responseObj = JSON.parse(responseText); + + // Check response format + expect(responseObj.filename).toBeDefined(); + expect(responseObj.description).toBe("generated content large!"); + + // Verify file was created + const expectedFilePath = path.join("/tmp/gh-aw/safe-outputs", responseObj.filename); + expect(fs.existsSync(expectedFilePath)).toBe(true); + + // Verify file content + const fileContent = fs.readFileSync(expectedFilePath, "utf8"); + expect(fileContent).toBe(largeBody); + + // Verify filename is SHA256 hash + extension + const hash = crypto.createHash("sha256").update(largeBody).digest("hex"); + expect(responseObj.filename).toMatch(new RegExp(`^${hash}\\.(txt|json|md)$`)); + + // Verify safe output was written with file reference + const outputLines = fs.readFileSync(tempOutputFile, "utf8").trim().split("\n"); + const lastOutput = JSON.parse(outputLines[outputLines.length - 1]); + expect(lastOutput.type).toBe("create_issue"); + expect(lastOutput.body).toContain("Content too large, saved to file:"); + expect(lastOutput.body).toContain(responseObj.filename); + + // Clean up created file + if (fs.existsSync(expectedFilePath)) { + fs.unlinkSync(expectedFilePath); + } + + resolve(); + }, 1000); + }, 1000); + }); + }); + + it("should handle normal content without writing to file", async () => { + // Set up environment + process.env.GH_AW_SAFE_OUTPUTS = tempOutputFile; + process.env.GH_AW_SAFE_OUTPUTS_CONFIG = fs.readFileSync(tempConfigFile, "utf8"); + + const serverPath = path.join(__dirname, "safe_outputs_mcp_server.cjs"); + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + child.kill(); + reject(new Error("Test timeout")); + }, 10000); + + const child = spawn("node", [serverPath], { + stdio: ["pipe", "pipe", "pipe"], + env: { ...process.env }, + }); + + let stderr = ""; + let stdout = ""; + + child.stderr.on("data", data => { + stderr += data.toString(); + }); + + child.stdout.on("data", data => { + stdout += data.toString(); + }); + + child.on("error", error => { + clearTimeout(timeout); + reject(error); + }); + + // Send initialization + const initMessage = + JSON.stringify({ + jsonrpc: "2.0", + id: 1, + method: "initialize", + params: { + protocolVersion: "2024-11-05", + capabilities: {}, + clientInfo: { name: "test-client", version: "1.0.0" }, + }, + }) + "\n"; + + child.stdin.write(initMessage); + + // Wait for initialization, then send normal content + setTimeout(() => { + const normalBody = "This is a normal issue body."; + + const toolCallMessage = + JSON.stringify({ + jsonrpc: "2.0", + id: 2, + method: "tools/call", + params: { + name: "create_issue", + arguments: { + title: "Test Issue", + body: normalBody, + }, + }, + }) + "\n"; + + child.stdin.write(toolCallMessage); + + // Wait for response + setTimeout(() => { + child.kill(); + clearTimeout(timeout); + + // Parse the response + const responses = stdout.trim().split("\n"); + const toolCallResponse = responses.find(line => { + try { + const parsed = JSON.parse(line); + return parsed.id === 2; + } catch { + return false; + } + }); + + expect(toolCallResponse).toBeDefined(); + + const parsed = JSON.parse(toolCallResponse); + expect(parsed.result).toBeDefined(); + expect(parsed.result.content).toBeDefined(); + expect(parsed.result.content.length).toBeGreaterThan(0); + + const responseText = parsed.result.content[0].text; + const responseObj = JSON.parse(responseText); + + // Normal response should just be success + expect(responseObj.result).toBe("success"); + + // Verify safe output was written normally + const outputLines = fs.readFileSync(tempOutputFile, "utf8").trim().split("\n"); + const lastOutput = JSON.parse(outputLines[outputLines.length - 1]); + expect(lastOutput.type).toBe("create_issue"); + expect(lastOutput.body).toBe(normalBody); + + resolve(); + }, 1000); + }, 1000); + }); + }); + + it("should detect JSON content and use .json extension", async () => { + // Set up environment + process.env.GH_AW_SAFE_OUTPUTS = tempOutputFile; + process.env.GH_AW_SAFE_OUTPUTS_CONFIG = fs.readFileSync(tempConfigFile, "utf8"); + + const serverPath = path.join(__dirname, "safe_outputs_mcp_server.cjs"); + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + child.kill(); + reject(new Error("Test timeout")); + }, 10000); + + const child = spawn("node", [serverPath], { + stdio: ["pipe", "pipe", "pipe"], + env: { ...process.env }, + }); + + let stderr = ""; + let stdout = ""; + + child.stderr.on("data", data => { + stderr += data.toString(); + }); + + child.stdout.on("data", data => { + stdout += data.toString(); + }); + + child.on("error", error => { + clearTimeout(timeout); + reject(error); + }); + + // Send initialization + const initMessage = + JSON.stringify({ + jsonrpc: "2.0", + id: 1, + method: "initialize", + params: { + protocolVersion: "2024-11-05", + capabilities: {}, + clientInfo: { name: "test-client", version: "1.0.0" }, + }, + }) + "\n"; + + child.stdin.write(initMessage); + + // Wait for initialization, then send JSON content + setTimeout(() => { + // Create large JSON content (> 16000 tokens) + const largeArray = Array(2000) + .fill(null) + .map((_, i) => ({ id: i, name: `Item ${i}`, data: "X".repeat(30) })); + const largeBody = JSON.stringify(largeArray, null, 2); + + const toolCallMessage = + JSON.stringify({ + jsonrpc: "2.0", + id: 2, + method: "tools/call", + params: { + name: "create_issue", + arguments: { + title: "Test Issue", + body: largeBody, + }, + }, + }) + "\n"; + + child.stdin.write(toolCallMessage); + + // Wait for response + setTimeout(() => { + child.kill(); + clearTimeout(timeout); + + // Parse the response + const responses = stdout.trim().split("\n"); + const toolCallResponse = responses.find(line => { + try { + const parsed = JSON.parse(line); + return parsed.id === 2; + } catch { + return false; + } + }); + + expect(toolCallResponse).toBeDefined(); + + const parsed = JSON.parse(toolCallResponse); + const responseText = parsed.result.content[0].text; + const responseObj = JSON.parse(responseText); + + // Check that filename has .json extension + expect(responseObj.filename).toMatch(/\.json$/); + + // Verify file was created with JSON extension + const expectedFilePath = path.join("/tmp/gh-aw/safe-outputs", responseObj.filename); + expect(fs.existsSync(expectedFilePath)).toBe(true); + + // Verify content is valid JSON + const fileContent = fs.readFileSync(expectedFilePath, "utf8"); + expect(() => JSON.parse(fileContent)).not.toThrow(); + + // Clean up + if (fs.existsSync(expectedFilePath)) { + fs.unlinkSync(expectedFilePath); + } + + resolve(); + }, 1000); + }, 1000); + }); + }); + + it("should detect Markdown content and use .md extension", async () => { + // Set up environment + process.env.GH_AW_SAFE_OUTPUTS = tempOutputFile; + process.env.GH_AW_SAFE_OUTPUTS_CONFIG = fs.readFileSync(tempConfigFile, "utf8"); + + const serverPath = path.join(__dirname, "safe_outputs_mcp_server.cjs"); + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + child.kill(); + reject(new Error("Test timeout")); + }, 10000); + + const child = spawn("node", [serverPath], { + stdio: ["pipe", "pipe", "pipe"], + env: { ...process.env }, + }); + + let stderr = ""; + let stdout = ""; + + child.stderr.on("data", data => { + stderr += data.toString(); + }); + + child.stdout.on("data", data => { + stdout += data.toString(); + }); + + child.on("error", error => { + clearTimeout(timeout); + reject(error); + }); + + // Send initialization + const initMessage = + JSON.stringify({ + jsonrpc: "2.0", + id: 1, + method: "initialize", + params: { + protocolVersion: "2024-11-05", + capabilities: {}, + clientInfo: { name: "test-client", version: "1.0.0" }, + }, + }) + "\n"; + + child.stdin.write(initMessage); + + // Wait for initialization, then send Markdown content + setTimeout(() => { + // Create large Markdown content (> 16000 tokens) + let largeBody = "# Large Markdown Document\n\n"; + for (let i = 0; i < 1000; i++) { + largeBody += `## Section ${i}\n\n`; + largeBody += "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ".repeat(20); + largeBody += "\n\n"; + largeBody += "```javascript\n"; + largeBody += "const example = 'code block';\n"; + largeBody += "console.log(example);\n"; + largeBody += "```\n\n"; + } + + const toolCallMessage = + JSON.stringify({ + jsonrpc: "2.0", + id: 2, + method: "tools/call", + params: { + name: "create_issue", + arguments: { + title: "Test Issue", + body: largeBody, + }, + }, + }) + "\n"; + + child.stdin.write(toolCallMessage); + + // Wait for response + setTimeout(() => { + child.kill(); + clearTimeout(timeout); + + // Parse the response + const responses = stdout.trim().split("\n"); + const toolCallResponse = responses.find(line => { + try { + const parsed = JSON.parse(line); + return parsed.id === 2; + } catch { + return false; + } + }); + + expect(toolCallResponse).toBeDefined(); + + const parsed = JSON.parse(toolCallResponse); + const responseText = parsed.result.content[0].text; + const responseObj = JSON.parse(responseText); + + // Check that filename has .md extension + expect(responseObj.filename).toMatch(/\.md$/); + + // Verify file was created with Markdown extension + const expectedFilePath = path.join("/tmp/gh-aw/safe-outputs", responseObj.filename); + expect(fs.existsSync(expectedFilePath)).toBe(true); + + // Verify content contains markdown + const fileContent = fs.readFileSync(expectedFilePath, "utf8"); + expect(fileContent).toContain("# Large Markdown Document"); + expect(fileContent).toContain("```javascript"); + + // Clean up + if (fs.existsSync(expectedFilePath)) { + fs.unlinkSync(expectedFilePath); + } + + resolve(); + }, 1000); + }, 1000); + }); + }); +}); diff --git a/pkg/workflow/js/safe_outputs_mcp_server.cjs b/pkg/workflow/js/safe_outputs_mcp_server.cjs index ccf88571f21..642b795e00a 100644 --- a/pkg/workflow/js/safe_outputs_mcp_server.cjs +++ b/pkg/workflow/js/safe_outputs_mcp_server.cjs @@ -190,6 +190,79 @@ function replyError(id, code, message) { writeMessage(res); } +/** + * Estimates token count from text using 4 chars per token estimate + * @param {string} text - The text to estimate tokens for + * @returns {number} Approximate token count + */ +function estimateTokens(text) { + if (!text) return 0; + return Math.ceil(text.length / 4); +} + +/** + * Determines file extension based on content type + * @param {string} content - The content to analyze + * @returns {string} File extension (e.g., '.json', '.txt', '.md') + */ +function getFileExtension(content) { + if (!content) return ".txt"; + + const trimmed = content.trim(); + + // Check if it's JSON + if (trimmed.startsWith("{") || trimmed.startsWith("[")) { + try { + JSON.parse(trimmed); + return ".json"; + } catch { + // Not valid JSON + } + } + + // Check if it's markdown (contains markdown syntax) + if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { + return ".md"; + } + + // Default to text + return ".txt"; +} + +/** + * Writes large content to a file and returns metadata + * @param {string} content - The content to write + * @returns {Object} Object with filename and description + */ +function writeLargeContentToFile(content) { + const logsDir = "/tmp/gh-aw/safe-outputs"; + + // Ensure directory exists + if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); + } + + // Generate SHA256 hash of content + const hash = crypto.createHash("sha256").update(content).digest("hex"); + + // Determine file extension + const ext = getFileExtension(content); + + // Create filename + const filename = `${hash}${ext}`; + const filepath = path.join(logsDir, filename); + + // Write content to file + fs.writeFileSync(filepath, content, "utf8"); + + debug(`Wrote large content (${content.length} chars) to ${filepath}`); + + return { + filename: filename, + description: "generated content large!", + }; +} + function appendSafeOutput(entry) { if (!outputFile) throw new Error("No output file configured"); // Normalize type to use underscores (convert any dashes to underscores) @@ -204,6 +277,46 @@ function appendSafeOutput(entry) { const defaultHandler = type => args => { const entry = { ...(args || {}), type }; + + // Check if any field in the entry has content exceeding 16000 tokens + let largeContent = null; + let largeFieldName = null; + const TOKEN_THRESHOLD = 16000; + + for (const [key, value] of Object.entries(entry)) { + if (typeof value === "string") { + const tokens = estimateTokens(value); + if (tokens > TOKEN_THRESHOLD) { + largeContent = value; + largeFieldName = key; + debug(`Field '${key}' has ${tokens} tokens (exceeds ${TOKEN_THRESHOLD})`); + break; + } + } + } + + if (largeContent && largeFieldName) { + // Write large content to file + const fileInfo = writeLargeContentToFile(largeContent); + + // Replace large field with file reference + entry[largeFieldName] = `[Content too large, saved to file: ${fileInfo.filename}]`; + + // Append modified entry to safe outputs + appendSafeOutput(entry); + + // Return file info to the agent + return { + content: [ + { + type: "text", + text: JSON.stringify(fileInfo), + }, + ], + }; + } + + // Normal case - no large content appendSafeOutput(entry); return { content: [ From b4b9651999f8b633a50024e9e54f802501129cb2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 02:21:21 +0000 Subject: [PATCH 4/6] Complete large content handling implementation All tests passing and manual verification complete Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/artifacts-summary.lock.yml | 2 +- .github/workflows/audit-workflows.lock.yml | 2 +- .github/workflows/brave.lock.yml | 2 +- .github/workflows/changeset-generator.lock.yml | 2 +- .github/workflows/ci-doctor.lock.yml | 2 +- .github/workflows/cli-version-checker.lock.yml | 2 +- .github/workflows/copilot-agent-analysis.lock.yml | 2 +- .github/workflows/daily-doc-updater.lock.yml | 2 +- .github/workflows/daily-news.lock.yml | 2 +- .github/workflows/dev-hawk.lock.yml | 2 +- .github/workflows/duplicate-code-detector.lock.yml | 2 +- .github/workflows/example-workflow-analyzer.lock.yml | 2 +- .github/workflows/github-mcp-tools-report.lock.yml | 2 +- .github/workflows/go-pattern-detector.lock.yml | 2 +- .github/workflows/issue-classifier.lock.yml | 2 +- .github/workflows/lockfile-stats.lock.yml | 2 +- .github/workflows/mcp-inspector.lock.yml | 2 +- .github/workflows/notion-issue-summary.lock.yml | 2 +- .github/workflows/pdf-summary.lock.yml | 2 +- .github/workflows/plan.lock.yml | 2 +- .github/workflows/poem-bot.lock.yml | 2 +- .github/workflows/q.lock.yml | 2 +- .github/workflows/repo-tree-map.lock.yml | 2 +- .github/workflows/research.lock.yml | 2 +- .github/workflows/scout.lock.yml | 2 +- .github/workflows/security-fix-pr.lock.yml | 2 +- .github/workflows/smoke-claude.lock.yml | 2 +- .github/workflows/smoke-codex.lock.yml | 2 +- .github/workflows/smoke-copilot.lock.yml | 2 +- .github/workflows/smoke-genaiscript.lock.yml | 2 +- .github/workflows/smoke-opencode.lock.yml | 2 +- .github/workflows/technical-doc-writer.lock.yml | 2 +- .github/workflows/tidy.lock.yml | 2 +- .github/workflows/unbloat-docs.lock.yml | 2 +- .github/workflows/video-analyzer.lock.yml | 2 +- 35 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index c03ff30565e..a35f5ad05e5 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -314,7 +314,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 4bb78966ecd..b3ac4340f04 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -464,7 +464,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index 1bf626fc083..c76ca92799b 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1322,7 +1322,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/changeset-generator.lock.yml b/.github/workflows/changeset-generator.lock.yml index 783a02bc2fb..fd50183cc9e 100644 --- a/.github/workflows/changeset-generator.lock.yml +++ b/.github/workflows/changeset-generator.lock.yml @@ -937,7 +937,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index cff2d413ea8..ad8b1c91ae4 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -714,7 +714,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index c3ef9f88608..2129a8211ed 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -430,7 +430,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index 8d7d47cf72e..d9534e5f5e0 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -453,7 +453,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index 7ad42f8b514..6f125e0d81f 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -444,7 +444,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 5cfdf0fc0b8..1e68c6a810c 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -341,7 +341,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index fa05b7d5376..9ad10b9839e 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -691,7 +691,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index b374c1e4e5a..dc48942c873 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -341,7 +341,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index 8dfb965fc67..5d561dd5d58 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -430,7 +430,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index bfedca7c45d..2c020c27088 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -442,7 +442,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 59587ca5439..c17692892de 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -429,7 +429,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 5016fdb2c89..ae2b449bcbc 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -1042,7 +1042,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index 315fa936922..630b987ceea 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -443,7 +443,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index 9fe0821787d..5fc3b4fa814 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -767,7 +767,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index b7d244b5a91..881296cb34e 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -314,7 +314,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index e8bbbf140f3..0d3b941cd45 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1235,7 +1235,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index 110c13ffe3d..fa866ed4eff 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -817,7 +817,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 887f32a5cbe..00b93e909b7 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1497,7 +1497,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index e31f642c2e8..c6b356ddc93 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1274,7 +1274,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index 58c62208cf2..256d5d95cca 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -312,7 +312,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index 0bdb31b5eb8..bbe0f185c6b 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -321,7 +321,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index f55b6eef6a1..c17f47a5b9b 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1513,7 +1513,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index 76f0a61c72a..3025a684898 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -442,7 +442,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 0b71570bdb7..0b0e0879d75 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -419,7 +419,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 98710769946..d70c65ad462 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -315,7 +315,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index b48e94f65be..25082924db5 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -310,7 +310,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/smoke-genaiscript.lock.yml b/.github/workflows/smoke-genaiscript.lock.yml index 3db944a9ecb..8eb61709734 100644 --- a/.github/workflows/smoke-genaiscript.lock.yml +++ b/.github/workflows/smoke-genaiscript.lock.yml @@ -300,7 +300,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml index a4e2715b8ae..c9de43cbad3 100644 --- a/.github/workflows/smoke-opencode.lock.yml +++ b/.github/workflows/smoke-opencode.lock.yml @@ -300,7 +300,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 79aab5d8c69..60fcb1b616b 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -842,7 +842,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index c7695d7a76f..693d15f6159 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -680,7 +680,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 3c6c4ce9c49..2657771bbcb 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1186,7 +1186,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index 9ee7c55550d..b6f2738d508 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -329,7 +329,7 @@ jobs: debug(`Wrote large content (${content.length} chars) to ${filepath}`); return { filename: filename, - description: "generated content large!" + description: "generated content large!", }; } function appendSafeOutput(entry) { From 911ce360bc71b613e03abf6710bf0b61c0c79bed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 03:03:58 +0000 Subject: [PATCH 5/6] Change file extension to always use .json and replace description with compact schema - Always use .json extension for all large content (MCP tools return JSON) - Replace static "generated content large!" with compact schema description - Schema describes structure: arrays show keys and item count, objects show keys - For non-JSON content, description is "text content" - Updated all tests to verify new behavior Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/artifacts-summary.lock.yml | 40 +++++++----- .github/workflows/audit-workflows.lock.yml | 40 +++++++----- .github/workflows/brave.lock.yml | 40 +++++++----- .../workflows/changeset-generator.lock.yml | 40 +++++++----- .github/workflows/ci-doctor.lock.yml | 40 +++++++----- .../workflows/cli-version-checker.lock.yml | 40 +++++++----- .../workflows/copilot-agent-analysis.lock.yml | 40 +++++++----- .github/workflows/daily-doc-updater.lock.yml | 40 +++++++----- .github/workflows/daily-news.lock.yml | 40 +++++++----- .github/workflows/dev-hawk.lock.yml | 40 +++++++----- .../duplicate-code-detector.lock.yml | 40 +++++++----- .../example-workflow-analyzer.lock.yml | 40 +++++++----- .../github-mcp-tools-report.lock.yml | 40 +++++++----- .../workflows/go-pattern-detector.lock.yml | 40 +++++++----- .github/workflows/issue-classifier.lock.yml | 40 +++++++----- .github/workflows/lockfile-stats.lock.yml | 40 +++++++----- .github/workflows/mcp-inspector.lock.yml | 40 +++++++----- .../workflows/notion-issue-summary.lock.yml | 40 +++++++----- .github/workflows/pdf-summary.lock.yml | 40 +++++++----- .github/workflows/plan.lock.yml | 40 +++++++----- .github/workflows/poem-bot.lock.yml | 40 +++++++----- .github/workflows/q.lock.yml | 40 +++++++----- .github/workflows/repo-tree-map.lock.yml | 40 +++++++----- .github/workflows/research.lock.yml | 40 +++++++----- .github/workflows/scout.lock.yml | 40 +++++++----- .github/workflows/security-fix-pr.lock.yml | 40 +++++++----- .github/workflows/smoke-claude.lock.yml | 40 +++++++----- .github/workflows/smoke-codex.lock.yml | 40 +++++++----- .github/workflows/smoke-copilot.lock.yml | 40 +++++++----- .github/workflows/smoke-genaiscript.lock.yml | 40 +++++++----- .github/workflows/smoke-opencode.lock.yml | 40 +++++++----- .../workflows/technical-doc-writer.lock.yml | 40 +++++++----- .github/workflows/tidy.lock.yml | 40 +++++++----- .github/workflows/unbloat-docs.lock.yml | 40 +++++++----- .github/workflows/video-analyzer.lock.yml | 40 +++++++----- .../safe_outputs_mcp_large_content.test.cjs | 30 ++++++--- pkg/workflow/js/safe_outputs_mcp_server.cjs | 62 +++++++++++-------- 37 files changed, 930 insertions(+), 562 deletions(-) diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index a35f5ad05e5..56e01439d95 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -286,20 +286,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -307,14 +317,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index b3ac4340f04..01df7f30dae 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -436,20 +436,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -457,14 +467,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index c76ca92799b..1d8580e35bb 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1294,20 +1294,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -1315,14 +1325,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/changeset-generator.lock.yml b/.github/workflows/changeset-generator.lock.yml index fd50183cc9e..f560f22c5e5 100644 --- a/.github/workflows/changeset-generator.lock.yml +++ b/.github/workflows/changeset-generator.lock.yml @@ -909,20 +909,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -930,14 +940,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index ad8b1c91ae4..be34b6b18ce 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -686,20 +686,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -707,14 +717,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 2129a8211ed..cba49013afc 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -402,20 +402,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -423,14 +433,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index d9534e5f5e0..c267bcf83cf 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -425,20 +425,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -446,14 +456,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index 6f125e0d81f..e6bc4cbad4c 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -416,20 +416,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -437,14 +447,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 1e68c6a810c..e6619194a88 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -313,20 +313,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -334,14 +344,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index 9ad10b9839e..ec2e81fee6f 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -663,20 +663,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -684,14 +694,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index dc48942c873..55bb8b5f522 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -313,20 +313,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -334,14 +344,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index 5d561dd5d58..e440704fd87 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -402,20 +402,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -423,14 +433,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index 2c020c27088..2207b09a332 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -414,20 +414,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -435,14 +445,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index c17692892de..e7f9e4ece72 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -401,20 +401,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -422,14 +432,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index ae2b449bcbc..78548f50412 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -1014,20 +1014,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -1035,14 +1045,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index 630b987ceea..d0404a101e9 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -415,20 +415,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -436,14 +446,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index 5fc3b4fa814..efe13851656 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -739,20 +739,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -760,14 +770,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index 881296cb34e..23bd2794d0b 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -286,20 +286,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -307,14 +317,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 0d3b941cd45..47f4a95e2dd 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1207,20 +1207,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -1228,14 +1238,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index fa866ed4eff..6c6130b3241 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -789,20 +789,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -810,14 +820,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 00b93e909b7..00aa7532f45 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1469,20 +1469,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -1490,14 +1500,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index c6b356ddc93..a85ded06189 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1246,20 +1246,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -1267,14 +1277,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index 256d5d95cca..2833b345010 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -284,20 +284,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -305,14 +315,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index bbe0f185c6b..f5845570885 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -293,20 +293,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -314,14 +324,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index c17f47a5b9b..745f95fab68 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1485,20 +1485,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -1506,14 +1516,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index 3025a684898..b73369213bc 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -414,20 +414,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -435,14 +445,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 0b0e0879d75..92935854305 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -391,20 +391,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -412,14 +422,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index d70c65ad462..78898689685 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -287,20 +287,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -308,14 +318,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 25082924db5..5a440842612 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -282,20 +282,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -303,14 +313,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/smoke-genaiscript.lock.yml b/.github/workflows/smoke-genaiscript.lock.yml index 8eb61709734..cda1548c194 100644 --- a/.github/workflows/smoke-genaiscript.lock.yml +++ b/.github/workflows/smoke-genaiscript.lock.yml @@ -272,20 +272,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -293,14 +303,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml index c9de43cbad3..1e7b4585601 100644 --- a/.github/workflows/smoke-opencode.lock.yml +++ b/.github/workflows/smoke-opencode.lock.yml @@ -272,20 +272,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -293,14 +303,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 60fcb1b616b..12fd53dd154 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -814,20 +814,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -835,14 +845,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 693d15f6159..0ba102e990e 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -652,20 +652,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -673,14 +683,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 2657771bbcb..aeda8384eb0 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1158,20 +1158,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -1179,14 +1189,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index b6f2738d508..e547b3848ce 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -301,20 +301,30 @@ jobs: if (!text) return 0; return Math.ceil(text.length / 4); } - function getFileExtension(content) { - if (!content) return ".txt"; - const trimmed = content.trim(); - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { + function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } + return `${typeof parsed}`; + } catch { + return "text content"; } - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; - } - return ".txt"; } function writeLargeContentToFile(content) { const logsDir = "/tmp/gh-aw/safe-outputs"; @@ -322,14 +332,14 @@ jobs: fs.mkdirSync(logsDir, { recursive: true }); } const hash = crypto.createHash("sha256").update(content).digest("hex"); - const ext = getFileExtension(content); - const filename = `${hash}${ext}`; + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); fs.writeFileSync(filepath, content, "utf8"); debug(`Wrote large content (${content.length} chars) to ${filepath}`); + const description = generateCompactSchema(content); return { filename: filename, - description: "generated content large!", + description: description, }; } function appendSafeOutput(entry) { diff --git a/pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs b/pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs index 55d49710277..5284134f09d 100644 --- a/pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs +++ b/pkg/workflow/js/safe_outputs_mcp_large_content.test.cjs @@ -134,7 +134,9 @@ describe("safe_outputs_mcp_server.cjs large content handling", () => { // Check response format expect(responseObj.filename).toBeDefined(); - expect(responseObj.description).toBe("generated content large!"); + expect(responseObj.description).toBeDefined(); + // Description should be a schema description, not the old static text + expect(responseObj.description).not.toBe("generated content large!"); // Verify file was created const expectedFilePath = path.join("/tmp/gh-aw/safe-outputs", responseObj.filename); @@ -144,9 +146,9 @@ describe("safe_outputs_mcp_server.cjs large content handling", () => { const fileContent = fs.readFileSync(expectedFilePath, "utf8"); expect(fileContent).toBe(largeBody); - // Verify filename is SHA256 hash + extension + // Verify filename is SHA256 hash + .json extension (always JSON now) const hash = crypto.createHash("sha256").update(largeBody).digest("hex"); - expect(responseObj.filename).toMatch(new RegExp(`^${hash}\\.(txt|json|md)$`)); + expect(responseObj.filename).toBe(`${hash}.json`); // Verify safe output was written with file reference const outputLines = fs.readFileSync(tempOutputFile, "utf8").trim().split("\n"); @@ -371,9 +373,14 @@ describe("safe_outputs_mcp_server.cjs large content handling", () => { const responseText = parsed.result.content[0].text; const responseObj = JSON.parse(responseText); - // Check that filename has .json extension + // Check that filename has .json extension (always JSON now) expect(responseObj.filename).toMatch(/\.json$/); + // Verify description contains schema info for array + expect(responseObj.description).toBeDefined(); + expect(responseObj.description).toContain("items"); // Should mention number of items + expect(responseObj.description).toContain("id, name, data"); // Should list keys + // Verify file was created with JSON extension const expectedFilePath = path.join("/tmp/gh-aw/safe-outputs", responseObj.filename); expect(fs.existsSync(expectedFilePath)).toBe(true); @@ -393,7 +400,7 @@ describe("safe_outputs_mcp_server.cjs large content handling", () => { }); }); - it("should detect Markdown content and use .md extension", async () => { + it("should always use .json extension even for non-JSON content", async () => { // Set up environment process.env.GH_AW_SAFE_OUTPUTS = tempOutputFile; process.env.GH_AW_SAFE_OUTPUTS_CONFIG = fs.readFileSync(tempConfigFile, "utf8"); @@ -444,7 +451,7 @@ describe("safe_outputs_mcp_server.cjs large content handling", () => { // Wait for initialization, then send Markdown content setTimeout(() => { - // Create large Markdown content (> 16000 tokens) + // Create large Markdown content (> 16000 tokens) - not valid JSON let largeBody = "# Large Markdown Document\n\n"; for (let i = 0; i < 1000; i++) { largeBody += `## Section ${i}\n\n`; @@ -494,14 +501,17 @@ describe("safe_outputs_mcp_server.cjs large content handling", () => { const responseText = parsed.result.content[0].text; const responseObj = JSON.parse(responseText); - // Check that filename has .md extension - expect(responseObj.filename).toMatch(/\.md$/); + // Check that filename has .json extension (always .json now) + expect(responseObj.filename).toMatch(/\.json$/); + + // For non-JSON content, description should be "text content" + expect(responseObj.description).toBe("text content"); - // Verify file was created with Markdown extension + // Verify file was created with .json extension const expectedFilePath = path.join("/tmp/gh-aw/safe-outputs", responseObj.filename); expect(fs.existsSync(expectedFilePath)).toBe(true); - // Verify content contains markdown + // Verify content is preserved const fileContent = fs.readFileSync(expectedFilePath, "utf8"); expect(fileContent).toContain("# Large Markdown Document"); expect(fileContent).toContain("```javascript"); diff --git a/pkg/workflow/js/safe_outputs_mcp_server.cjs b/pkg/workflow/js/safe_outputs_mcp_server.cjs index 642b795e00a..e2fdc863edd 100644 --- a/pkg/workflow/js/safe_outputs_mcp_server.cjs +++ b/pkg/workflow/js/safe_outputs_mcp_server.cjs @@ -201,32 +201,40 @@ function estimateTokens(text) { } /** - * Determines file extension based on content type - * @param {string} content - The content to analyze - * @returns {string} File extension (e.g., '.json', '.txt', '.md') + * Generates a compact schema description from JSON content + * @param {string} content - The JSON content to analyze + * @returns {string} Compact schema description for jq/agent */ -function getFileExtension(content) { - if (!content) return ".txt"; - - const trimmed = content.trim(); +function generateCompactSchema(content) { + try { + const parsed = JSON.parse(content); - // Check if it's JSON - if (trimmed.startsWith("{") || trimmed.startsWith("[")) { - try { - JSON.parse(trimmed); - return ".json"; - } catch { - // Not valid JSON + // Generate a compact schema based on the structure + if (Array.isArray(parsed)) { + if (parsed.length === 0) { + return "[]"; + } + // For arrays, describe the first element's structure + const firstItem = parsed[0]; + if (typeof firstItem === "object" && firstItem !== null) { + const keys = Object.keys(firstItem); + return `[{${keys.join(", ")}}] (${parsed.length} items)`; + } + return `[${typeof firstItem}] (${parsed.length} items)`; + } else if (typeof parsed === "object" && parsed !== null) { + // For objects, list top-level keys + const keys = Object.keys(parsed); + if (keys.length > 10) { + return `{${keys.slice(0, 10).join(", ")}, ...} (${keys.length} keys)`; + } + return `{${keys.join(", ")}}`; } - } - // Check if it's markdown (contains markdown syntax) - if (trimmed.includes("# ") || trimmed.includes("## ") || trimmed.includes("```")) { - return ".md"; + return `${typeof parsed}`; + } catch { + // If not valid JSON, return generic description + return "text content"; } - - // Default to text - return ".txt"; } /** @@ -245,11 +253,8 @@ function writeLargeContentToFile(content) { // Generate SHA256 hash of content const hash = crypto.createHash("sha256").update(content).digest("hex"); - // Determine file extension - const ext = getFileExtension(content); - - // Create filename - const filename = `${hash}${ext}`; + // MCP tools return JSON, so always use .json extension + const filename = `${hash}.json`; const filepath = path.join(logsDir, filename); // Write content to file @@ -257,9 +262,12 @@ function writeLargeContentToFile(content) { debug(`Wrote large content (${content.length} chars) to ${filepath}`); + // Generate compact schema description for jq/agent + const description = generateCompactSchema(content); + return { filename: filename, - description: "generated content large!", + description: description, }; } From 0777dbce59b1da890d9025ab3b84dfbc6e0198ab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 21 Oct 2025 03:24:31 +0000 Subject: [PATCH 6/6] Add changeset for large MCP output handling feature --- .changeset/patch-large-mcp-output-handling.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/patch-large-mcp-output-handling.md diff --git a/.changeset/patch-large-mcp-output-handling.md b/.changeset/patch-large-mcp-output-handling.md new file mode 100644 index 00000000000..0d627f65165 --- /dev/null +++ b/.changeset/patch-large-mcp-output-handling.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Add automatic file writing for large MCP tool outputs exceeding 16,000 tokens with compact schema descriptions