diff --git a/.changeset/patch-add-push-repo-memory-tool.md b/.changeset/patch-add-push-repo-memory-tool.md new file mode 100644 index 00000000000..bab93da7730 --- /dev/null +++ b/.changeset/patch-add-push-repo-memory-tool.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Added the new `push_repo_memory` safe output tool so workflows can validate repo memory limits early and wired it into the setup prompts/tests. diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml index b57a4db6c9e..e167ef9f13e 100644 --- a/.github/workflows/agent-performance-analyzer.lock.yml +++ b/.github/workflows/agent-performance-analyzer.lock.yml @@ -408,7 +408,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":10},"create_discussion":{"expires":24,"max":2},"create_issue":{"expires":48,"group":true,"max":5},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":10},"create_discussion":{"expires":24,"max":2},"create_issue":{"expires":48,"group":true,"max":5},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -624,6 +624,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 6253a3d3b17..62dd87c79d2 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -465,7 +465,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}} + {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"upload_asset":{"max":0}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -620,6 +620,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml index b89b2602a37..9481717e9f3 100644 --- a/.github/workflows/code-scanning-fixer.lock.yml +++ b/.github/workflows/code-scanning-fixer.lock.yml @@ -354,7 +354,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_labels":{"allowed":["agentic-campaign","z_campaign_security-alert-burndown"],"max":3},"create_pull_request":{"expires":48,"max":1,"reviewers":["copilot"],"title_prefix":"[code-scanning-fix] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"add_labels":{"allowed":["agentic-campaign","z_campaign_security-alert-burndown"],"max":3},"create_pull_request":{"expires":48,"max":1,"reviewers":["copilot"],"title_prefix":"[code-scanning-fix] "},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/campaigns","id":"campaigns","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -528,6 +528,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index a36fc1be151..bac503d9ebb 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -396,7 +396,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -526,6 +526,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/copilot-cli-deep-research.lock.yml b/.github/workflows/copilot-cli-deep-research.lock.yml index fe93a4449e7..02623be2527 100644 --- a/.github/workflows/copilot-cli-deep-research.lock.yml +++ b/.github/workflows/copilot-cli-deep-research.lock.yml @@ -353,7 +353,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":204800,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -483,6 +483,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml index 77b3039406a..9464f2a2242 100644 --- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml +++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml @@ -422,7 +422,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}} + {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"upload_asset":{"max":0}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -577,6 +577,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml index d486021f73c..4067b5f2731 100644 --- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml +++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml @@ -388,7 +388,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -518,6 +518,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index 35525e2d7ad..3a8db462fbc 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -433,7 +433,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}} + {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"upload_asset":{"max":0}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -588,6 +588,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml index e444f70d111..34c796d9d14 100644 --- a/.github/workflows/daily-cli-performance.lock.yml +++ b/.github/workflows/daily-cli-performance.lock.yml @@ -358,7 +358,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":5},"create_issue":{"expires":48,"group":true,"max":3},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":5},"create_issue":{"expires":48,"group":true,"max":3},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":512000,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -540,6 +540,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index 170c6117b82..42f41550615 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -407,7 +407,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}} + {"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"upload_asset":{"max":0}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -562,6 +562,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/daily-copilot-token-report.lock.yml b/.github/workflows/daily-copilot-token-report.lock.yml index b4266c72779..ea48b8453dc 100644 --- a/.github/workflows/daily-copilot-token-report.lock.yml +++ b/.github/workflows/daily-copilot-token-report.lock.yml @@ -430,7 +430,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}} + {"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"upload_asset":{"max":0}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -585,6 +585,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 2520ffb5fd8..450f9964ae8 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -475,7 +475,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}} + {"create_discussion":{"expires":72,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"upload_asset":{"max":0}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -630,6 +630,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml index 4e28a0e709e..19201c18b5d 100644 --- a/.github/workflows/daily-testify-uber-super-expert.lock.yml +++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml @@ -363,7 +363,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"create_issue":{"expires":48,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":51200,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -508,6 +508,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml index cda13b6daa4..17601ca6d7b 100644 --- a/.github/workflows/deep-report.lock.yml +++ b/.github/workflows/deep-report.lock.yml @@ -449,7 +449,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":168,"max":1},"create_issue":{"expires":48,"group":true,"max":3},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}} + {"create_discussion":{"expires":168,"max":1},"create_issue":{"expires":48,"group":true,"max":3},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":1048576,"max_patch_size":10240}]},"upload_asset":{"max":0}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -653,6 +653,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/delight.lock.yml b/.github/workflows/delight.lock.yml index 29691ebda4e..602dd6b56ca 100644 --- a/.github/workflows/delight.lock.yml +++ b/.github/workflows/delight.lock.yml @@ -358,7 +358,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":168,"max":1},"create_issue":{"expires":48,"group":true,"max":2},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"expires":168,"max":1},"create_issue":{"expires":48,"group":true,"max":2},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -537,6 +537,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index a74ec6c713b..35157994599 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -384,7 +384,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":168,"max":1},"create_pull_request":{"expires":48,"max":1,"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"expires":168,"max":1},"create_pull_request":{"expires":48,"max":1,"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -563,6 +563,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/discussion-task-miner.lock.yml b/.github/workflows/discussion-task-miner.lock.yml index ddbb66fbfa6..33c6b521b83 100644 --- a/.github/workflows/discussion-task-miner.lock.yml +++ b/.github/workflows/discussion-task-miner.lock.yml @@ -349,7 +349,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":3},"create_issue":{"expires":24,"group":true,"max":5},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":3},"create_issue":{"expires":24,"group":true,"max":5},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -531,6 +531,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/firewall-escape.lock.yml b/.github/workflows/firewall-escape.lock.yml index 29100220eb6..1b0609447aa 100644 --- a/.github/workflows/firewall-escape.lock.yml +++ b/.github/workflows/firewall-escape.lock.yml @@ -383,7 +383,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"create_discussion":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":50,"max_file_size":524288,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -513,6 +513,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index 96b00004117..c8ac0aaefcb 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -393,7 +393,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_pull_request":{"expires":48,"max":1,"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"create_pull_request":{"expires":48,"max":1,"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -538,6 +538,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml index e944423b52c..9d4011c582f 100644 --- a/.github/workflows/pr-triage-agent.lock.yml +++ b/.github/workflows/pr-triage-agent.lock.yml @@ -340,7 +340,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":50},"add_labels":{"max":100},"create_issue":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":50},"add_labels":{"max":100},"create_issue":{"expires":24,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -551,6 +551,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/security-alert-burndown.campaign.g.lock.yml b/.github/workflows/security-alert-burndown.campaign.g.lock.yml index f80453442a3..e5d64468ea7 100644 --- a/.github/workflows/security-alert-burndown.campaign.g.lock.yml +++ b/.github/workflows/security-alert-burndown.campaign.g.lock.yml @@ -367,7 +367,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":3},"create_issue":{"max":1},"create_project_status_update":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"update_project":{"max":10}} + {"add_comment":{"max":3},"create_issue":{"max":1},"create_project_status_update":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/campaigns","id":"campaigns","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]},"update_project":{"max":10}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -745,6 +745,21 @@ jobs: "type": "object" }, "name": "create_project_status_update" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml index a1ead447ecb..0274f8b5f75 100644 --- a/.github/workflows/security-compliance.lock.yml +++ b/.github/workflows/security-compliance.lock.yml @@ -377,7 +377,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_issue":{"expires":48,"group":true,"max":100},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"create_issue":{"expires":48,"group":true,"max":100},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -522,6 +522,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index c0466a227b3..9dd971ec39e 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -411,7 +411,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":1},"create_pull_request":{"expires":48,"max":1,"reviewers":["copilot"],"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1},"upload_asset":{"max":0}} + {"add_comment":{"max":1},"create_pull_request":{"expires":48,"max":1,"reviewers":["copilot"],"title_prefix":"[docs] "},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]},"upload_asset":{"max":0}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -618,6 +618,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml index 50f7d6a060e..b7f82c3089d 100644 --- a/.github/workflows/workflow-health-manager.lock.yml +++ b/.github/workflows/workflow-health-manager.lock.yml @@ -358,7 +358,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":15},"create_issue":{"expires":24,"group":true,"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1},"update_issue":{"max":5}} + {"add_comment":{"max":15},"create_issue":{"expires":24,"group":true,"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":102400,"max_patch_size":10240}]},"update_issue":{"max":5}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -612,6 +612,21 @@ jobs: "type": "object" }, "name": "missing_data" + }, + { + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "memory_id": { + "description": "Memory identifier to validate. Defaults to 'default' if not specified.", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "push_repo_memory" } ] GH_AW_SAFE_OUTPUTS_TOOLS_EOF diff --git a/actions/setup/js/safe_outputs_handlers.cjs b/actions/setup/js/safe_outputs_handlers.cjs index fe5b1d73346..c8588efc571 100644 --- a/actions/setup/js/safe_outputs_handlers.cjs +++ b/actions/setup/js/safe_outputs_handlers.cjs @@ -1,4 +1,5 @@ // @ts-check +/// const fs = require("fs"); const path = require("path"); @@ -470,6 +471,181 @@ function createHandlers(server, appendSafeOutput, config = {}) { }; }; + /** + * Handler for push_repo_memory tool + * Validates that memory files in the configured memory directory are within size limits. + * Returns an error if any file or the total size exceeds the configured limits, + * with guidance to reduce memory size before the workflow completes. + */ + const pushRepoMemoryHandler = args => { + const memoryId = (args && args.memory_id) || "default"; + const repoMemoryConfig = config.push_repo_memory; + + if (!repoMemoryConfig || !repoMemoryConfig.memories || repoMemoryConfig.memories.length === 0) { + return { + content: [ + { + type: "text", + text: JSON.stringify({ result: "success", message: "No repo-memory configured." }), + }, + ], + }; + } + + // Find the memory config for the requested memory_id + const memoryConf = repoMemoryConfig.memories.find(m => m.id === memoryId); + if (!memoryConf) { + const availableIds = repoMemoryConfig.memories.map(m => m.id).join(", "); + return { + content: [ + { + type: "text", + text: JSON.stringify({ + result: "error", + error: `Memory ID '${memoryId}' not found. Available memory IDs: ${availableIds}`, + }), + }, + ], + isError: true, + }; + } + + const memoryDir = memoryConf.dir; + const maxFileSize = memoryConf.max_file_size || 10240; + const maxPatchSize = memoryConf.max_patch_size || 10240; + const maxFileCount = memoryConf.max_file_count || 100; + // Allow 20% overhead for git diff format (headers, context lines, etc.) + const effectiveMaxPatchSize = Math.floor(maxPatchSize * 1.2); + + if (!fs.existsSync(memoryDir)) { + return { + content: [ + { + type: "text", + text: JSON.stringify({ result: "success", message: `Memory directory '${memoryDir}' does not exist yet. No files to validate.` }), + }, + ], + }; + } + + // Recursively scan all files in the memory directory + /** @type {Array<{relativePath: string, size: number}>} */ + const files = []; + + /** + * @param {string} dirPath + * @param {string} relativePath + */ + function scanDir(dirPath, relativePath) { + const entries = fs.readdirSync(dirPath, { withFileTypes: true }); + for (const entry of entries) { + const fullPath = path.join(dirPath, entry.name); + const relPath = relativePath ? path.join(relativePath, entry.name) : entry.name; + if (entry.isDirectory()) { + scanDir(fullPath, relPath); + } else if (entry.isFile()) { + const stats = fs.statSync(fullPath); + files.push({ relativePath: relPath.replace(/\\/g, "/"), size: stats.size }); + } + } + } + + try { + scanDir(memoryDir, ""); + } catch (/** @type {any} */ error) { + return { + content: [ + { + type: "text", + text: JSON.stringify({ + result: "error", + error: `Failed to scan memory directory: ${getErrorMessage(error)}`, + }), + }, + ], + isError: true, + }; + } + + // Check individual file sizes + const oversizedFiles = files.filter(f => f.size > maxFileSize); + if (oversizedFiles.length > 0) { + const details = oversizedFiles.map(f => ` - ${f.relativePath} (${f.size} bytes > ${maxFileSize} bytes limit)`).join("\n"); + return { + content: [ + { + type: "text", + text: JSON.stringify({ + result: "error", + error: + `${oversizedFiles.length} file(s) exceed the maximum file size of ${maxFileSize} bytes (${Math.ceil(maxFileSize / 1024)} KB):\n${details}\n\n` + + `Please reduce the size of these files before the workflow completes. Consider summarizing or truncating the content.`, + }), + }, + ], + isError: true, + }; + } + + // Check file count + if (files.length > maxFileCount) { + return { + content: [ + { + type: "text", + text: JSON.stringify({ + result: "error", + error: `Too many files in memory: ${files.length} files exceeds the limit of ${maxFileCount} files.\n\n` + `Please reduce the number of files in '${memoryDir}' before the workflow completes.`, + }), + }, + ], + isError: true, + }; + } + + // Check total size. The effective limit allows 20% overhead to account for + // git diff format overhead (headers, context lines, metadata). This mirrors + // the same calculation in push_repo_memory.cjs. The totalSize is the raw + // sum of file sizes; it is compared against the overhead-adjusted limit. + const totalSize = files.reduce((sum, f) => sum + f.size, 0); + const totalSizeKb = Math.ceil(totalSize / 1024); + const effectiveMaxKb = Math.floor(effectiveMaxPatchSize / 1024); + + core.debug(`push_repo_memory validation: ${files.length} files, total ${totalSize} bytes, effective limit ${effectiveMaxPatchSize} bytes`); + + if (totalSize > effectiveMaxPatchSize) { + return { + content: [ + { + type: "text", + text: JSON.stringify({ + result: "error", + error: + `Total memory size (${totalSizeKb} KB) exceeds the allowed limit of ${effectiveMaxKb} KB ` + + `(configured limit: ${Math.floor(maxPatchSize / 1024)} KB with 20% overhead for git diff format).\n\n` + + `Please reduce the total size of files in '${memoryDir}' before the workflow completes. ` + + `Consider: summarizing notes instead of keeping full history, removing outdated entries, or compressing data. ` + + `Then call push_repo_memory again to verify the size is within limits.`, + }), + }, + ], + isError: true, + }; + } + + return { + content: [ + { + type: "text", + text: JSON.stringify({ + result: "success", + message: `Memory validation passed: ${files.length} file(s), ${totalSizeKb} KB total (limit: ${effectiveMaxKb} KB with 20% overhead).`, + }), + }, + ], + }; + }; + /** * Handler for create_project tool * Auto-generates a temporary ID if not provided and returns it to the agent @@ -565,6 +741,7 @@ function createHandlers(server, appendSafeOutput, config = {}) { uploadAssetHandler, createPullRequestHandler, pushToPullRequestBranchHandler, + pushRepoMemoryHandler, createProjectHandler, addCommentHandler, }; diff --git a/actions/setup/js/safe_outputs_handlers.test.cjs b/actions/setup/js/safe_outputs_handlers.test.cjs index f8bcd54a126..32604538db3 100644 --- a/actions/setup/js/safe_outputs_handlers.test.cjs +++ b/actions/setup/js/safe_outputs_handlers.test.cjs @@ -447,6 +447,7 @@ describe("safe_outputs_handlers", () => { expect(handlers.uploadAssetHandler).toBeDefined(); expect(handlers.createPullRequestHandler).toBeDefined(); expect(handlers.pushToPullRequestBranchHandler).toBeDefined(); + expect(handlers.pushRepoMemoryHandler).toBeDefined(); expect(handlers.addCommentHandler).toBeDefined(); }); @@ -506,4 +507,140 @@ describe("safe_outputs_handlers", () => { expect(() => handlers.addCommentHandler({ body: longBody })).toThrow(); }); }); + + describe("pushRepoMemoryHandler", () => { + let memoryDir; + + beforeEach(() => { + const testId = Math.random().toString(36).substring(7); + memoryDir = `/tmp/test-repo-memory-${testId}`; + }); + + afterEach(() => { + try { + if (fs.existsSync(memoryDir)) { + fs.rmSync(memoryDir, { recursive: true, force: true }); + } + } catch (_error) { + // Ignore cleanup errors + } + }); + + function makeHandlersWithMemory(overrides = {}) { + const memConf = { + id: "default", + dir: memoryDir, + max_file_size: 1024, // 1 KB + max_patch_size: 2048, // 2 KB + max_file_count: 5, + ...overrides, + }; + return createHandlers(mockServer, mockAppendSafeOutput, { + push_repo_memory: { memories: [memConf] }, + }); + } + + it("should return success when no repo-memory is configured", () => { + const h = createHandlers(mockServer, mockAppendSafeOutput, {}); + const result = h.pushRepoMemoryHandler({}); + const data = JSON.parse(result.content[0].text); + expect(data.result).toBe("success"); + expect(data.message).toContain("No repo-memory configured"); + }); + + it("should return error for unknown memory_id", () => { + const h = makeHandlersWithMemory(); + fs.mkdirSync(memoryDir, { recursive: true }); + const result = h.pushRepoMemoryHandler({ memory_id: "nonexistent" }); + expect(result.isError).toBe(true); + const data = JSON.parse(result.content[0].text); + expect(data.result).toBe("error"); + expect(data.error).toContain("'nonexistent' not found"); + expect(data.error).toContain("default"); + }); + + it("should return success when memory directory does not exist yet", () => { + const h = makeHandlersWithMemory(); + // memoryDir not created + const result = h.pushRepoMemoryHandler({ memory_id: "default" }); + const data = JSON.parse(result.content[0].text); + expect(data.result).toBe("success"); + expect(data.message).toContain("does not exist yet"); + }); + + it("should return success for valid files within limits", () => { + const h = makeHandlersWithMemory(); + fs.mkdirSync(memoryDir, { recursive: true }); + fs.writeFileSync(path.join(memoryDir, "state.json"), "x".repeat(100)); + const result = h.pushRepoMemoryHandler({ memory_id: "default" }); + const data = JSON.parse(result.content[0].text); + expect(data.result).toBe("success"); + expect(data.message).toContain("validation passed"); + }); + + it("should return error when a file exceeds max_file_size", () => { + const h = makeHandlersWithMemory({ max_file_size: 100 }); + fs.mkdirSync(memoryDir, { recursive: true }); + fs.writeFileSync(path.join(memoryDir, "big.json"), "x".repeat(200)); + const result = h.pushRepoMemoryHandler({ memory_id: "default" }); + expect(result.isError).toBe(true); + const data = JSON.parse(result.content[0].text); + expect(data.result).toBe("error"); + expect(data.error).toContain("big.json"); + expect(data.error).toContain("200 bytes"); + }); + + it("should return error when file count exceeds max_file_count", () => { + const h = makeHandlersWithMemory({ max_file_count: 2 }); + fs.mkdirSync(memoryDir, { recursive: true }); + for (let i = 0; i < 3; i++) { + fs.writeFileSync(path.join(memoryDir, `file${i}.json`), "x".repeat(10)); + } + const result = h.pushRepoMemoryHandler({ memory_id: "default" }); + expect(result.isError).toBe(true); + const data = JSON.parse(result.content[0].text); + expect(data.result).toBe("error"); + expect(data.error).toContain("Too many files"); + expect(data.error).toContain("3 files"); + }); + + it("should return error when total size exceeds effective max_patch_size", () => { + // max_patch_size = 500 bytes, effective limit = floor(500 * 1.2) = 600 bytes + const h = makeHandlersWithMemory({ max_patch_size: 500, max_file_size: 1024 * 1024 }); + fs.mkdirSync(memoryDir, { recursive: true }); + // Write two files totaling 650 bytes (above the 600 byte effective limit) + fs.writeFileSync(path.join(memoryDir, "a.json"), "x".repeat(350)); + fs.writeFileSync(path.join(memoryDir, "b.json"), "x".repeat(300)); + const result = h.pushRepoMemoryHandler({ memory_id: "default" }); + expect(result.isError).toBe(true); + const data = JSON.parse(result.content[0].text); + expect(data.result).toBe("error"); + expect(data.error).toContain("exceeds the allowed limit"); + expect(data.error).toContain("push_repo_memory again"); + }); + + it("should use 'default' memory_id when memory_id is not specified", () => { + const h = makeHandlersWithMemory(); + fs.mkdirSync(memoryDir, { recursive: true }); + fs.writeFileSync(path.join(memoryDir, "notes.md"), "hello"); + const result = h.pushRepoMemoryHandler({}); // no memory_id + const data = JSON.parse(result.content[0].text); + expect(data.result).toBe("success"); + }); + + it("should scan files recursively in subdirectories", () => { + // max_patch_size = 500 bytes, effective limit = 600 bytes + const h = makeHandlersWithMemory({ max_patch_size: 500, max_file_size: 1024 * 1024 }); + const subDir = path.join(memoryDir, "history"); + fs.mkdirSync(subDir, { recursive: true }); + // Write a nested file that pushes total above effective limit + fs.writeFileSync(path.join(subDir, "log.jsonl"), "x".repeat(700)); + const result = h.pushRepoMemoryHandler({ memory_id: "default" }); + expect(result.isError).toBe(true); + const data = JSON.parse(result.content[0].text); + expect(data.result).toBe("error"); + // The nested file path should appear correctly + expect(data.error).toContain("exceeds the allowed limit"); + }); + }); }); diff --git a/actions/setup/js/safe_outputs_tools.json b/actions/setup/js/safe_outputs_tools.json index 0be865a5a9c..d21a682c1d8 100644 --- a/actions/setup/js/safe_outputs_tools.json +++ b/actions/setup/js/safe_outputs_tools.json @@ -1267,5 +1267,20 @@ }, "additionalProperties": false } + }, + { + "name": "push_repo_memory", + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "type": "object", + "required": [], + "properties": { + "memory_id": { + "type": "string", + "description": "Memory identifier to validate. Defaults to 'default' if not specified." + } + }, + "additionalProperties": false + } } ] diff --git a/actions/setup/js/safe_outputs_tools_loader.cjs b/actions/setup/js/safe_outputs_tools_loader.cjs index c1b9d3d313a..7d50a9638ac 100644 --- a/actions/setup/js/safe_outputs_tools_loader.cjs +++ b/actions/setup/js/safe_outputs_tools_loader.cjs @@ -56,6 +56,7 @@ function attachHandlers(tools, handlers) { const handlerMap = { create_pull_request: handlers.createPullRequestHandler, push_to_pull_request_branch: handlers.pushToPullRequestBranchHandler, + push_repo_memory: handlers.pushRepoMemoryHandler, upload_asset: handlers.uploadAssetHandler, create_project: handlers.createProjectHandler, add_comment: handlers.addCommentHandler, diff --git a/actions/setup/md/repo_memory_prompt.md b/actions/setup/md/repo_memory_prompt.md index a4dd0f9c32a..9d8403bf20a 100644 --- a/actions/setup/md/repo_memory_prompt.md +++ b/actions/setup/md/repo_memory_prompt.md @@ -18,4 +18,6 @@ Examples of what you can store: - `__GH_AW_MEMORY_DIR__history/` - organized history files in subdirectories (with allowed file types) Feel free to create, read, update, and organize files in this folder as needed for your tasks, using only the allowed file types. + +**Important**: If the `push_repo_memory` tool is available in your tool list, call it after writing or updating memory files to validate that the total memory size is within the configured limits. If the tool returns an error, reduce the size of your memory files (e.g., summarize notes, remove outdated entries) and try again before completing your task. If the tool is not available, you can skip this validation step. diff --git a/actions/setup/md/repo_memory_prompt_multi.md b/actions/setup/md/repo_memory_prompt_multi.md index 7d1741b3476..c62ee559266 100644 --- a/actions/setup/md/repo_memory_prompt_multi.md +++ b/actions/setup/md/repo_memory_prompt_multi.md @@ -20,4 +20,6 @@ Examples of what you can store: - `/tmp/gh-aw/repo-memory/history/` - organized history files (with allowed file types) Feel free to create, read, update, and organize files in these folders as needed for your tasks, using only the allowed file types. + +**Important**: After writing or updating memory files, if the `push_repo_memory` tool is available, call it (with the appropriate `memory_id`) to validate that the total memory size is within the configured limits. If the tool returns an error, reduce the size of your memory files (e.g., summarize notes, remove outdated entries) and try again before completing your task. diff --git a/pkg/workflow/js/safe_outputs_tools.json b/pkg/workflow/js/safe_outputs_tools.json index 6740c37cd34..c07ab295fe3 100644 --- a/pkg/workflow/js/safe_outputs_tools.json +++ b/pkg/workflow/js/safe_outputs_tools.json @@ -1518,5 +1518,20 @@ }, "additionalProperties": false } + }, + { + "name": "push_repo_memory", + "description": "Validate repo-memory files are within configured size limits before the workflow completes. Call this after writing files to memory to check that the total size is within limits. Returns an error if files are too large, with guidance on how to reduce memory size so the memory can be saved successfully.", + "inputSchema": { + "type": "object", + "required": [], + "properties": { + "memory_id": { + "type": "string", + "description": "Memory identifier to validate. Defaults to 'default' if not specified." + } + }, + "additionalProperties": false + } } ] diff --git a/pkg/workflow/safe_outputs_config_generation_test.go b/pkg/workflow/safe_outputs_config_generation_test.go index 4fa2f50820b..c5777dbe9fb 100644 --- a/pkg/workflow/safe_outputs_config_generation_test.go +++ b/pkg/workflow/safe_outputs_config_generation_test.go @@ -453,3 +453,96 @@ func TestGenerateSafeOutputsConfigCreatePullRequestBackwardCompat(t *testing.T) _, hasAllowedRepos := prConfig["allowed_repos"] assert.False(t, hasAllowedRepos, "allowed_repos should not be present when not configured") } + +// TestGenerateSafeOutputsConfigRepoMemory tests that generateSafeOutputsConfig includes +// push_repo_memory configuration with the expected memories entries when RepoMemoryConfig is present. +func TestGenerateSafeOutputsConfigRepoMemory(t *testing.T) { + data := &WorkflowData{ + SafeOutputs: &SafeOutputsConfig{}, + RepoMemoryConfig: &RepoMemoryConfig{ + Memories: []RepoMemoryEntry{ + { + ID: "default", + MaxFileSize: 5120, + MaxPatchSize: 20480, + MaxFileCount: 50, + }, + { + ID: "notes", + MaxFileSize: 2048, + MaxPatchSize: 8192, + MaxFileCount: 20, + }, + }, + }, + } + + result := generateSafeOutputsConfig(data) + require.NotEmpty(t, result, "Expected non-empty config") + + var parsed map[string]any + require.NoError(t, json.Unmarshal([]byte(result), &parsed), "Result must be valid JSON") + + pushRepoMemory, ok := parsed["push_repo_memory"].(map[string]any) + require.True(t, ok, "Expected push_repo_memory key in config") + + memories, ok := pushRepoMemory["memories"].([]any) + require.True(t, ok, "Expected memories to be an array") + require.Len(t, memories, 2, "Expected 2 memory entries") + + // Check first memory entry + mem0, ok := memories[0].(map[string]any) + require.True(t, ok, "First memory entry should be a map") + assert.Equal(t, "default", mem0["id"], "First memory id should match") + assert.Equal(t, "/tmp/gh-aw/repo-memory/default", mem0["dir"], "First memory dir should be correct") + assert.InDelta(t, float64(5120), mem0["max_file_size"], 0.0001, "First memory max_file_size should match") + assert.InDelta(t, float64(20480), mem0["max_patch_size"], 0.0001, "First memory max_patch_size should match") + assert.InDelta(t, float64(50), mem0["max_file_count"], 0.0001, "First memory max_file_count should match") + + // Check second memory entry + mem1, ok := memories[1].(map[string]any) + require.True(t, ok, "Second memory entry should be a map") + assert.Equal(t, "notes", mem1["id"], "Second memory id should match") + assert.Equal(t, "/tmp/gh-aw/repo-memory/notes", mem1["dir"], "Second memory dir should be correct") + assert.InDelta(t, float64(2048), mem1["max_file_size"], 0.0001, "Second memory max_file_size should match") +} + +// TestGenerateSafeOutputsConfigNoRepoMemory tests that push_repo_memory is absent +// from the config when RepoMemoryConfig is not present. +func TestGenerateSafeOutputsConfigNoRepoMemory(t *testing.T) { + data := &WorkflowData{ + SafeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{}, + }, + RepoMemoryConfig: nil, + } + + result := generateSafeOutputsConfig(data) + require.NotEmpty(t, result, "Expected non-empty config") + + var parsed map[string]any + require.NoError(t, json.Unmarshal([]byte(result), &parsed), "Result must be valid JSON") + + _, hasPushRepoMemory := parsed["push_repo_memory"] + assert.False(t, hasPushRepoMemory, "push_repo_memory should not be present when RepoMemoryConfig is nil") +} + +// TestGenerateSafeOutputsConfigEmptyRepoMemory tests that push_repo_memory is absent +// from the config when RepoMemoryConfig has no memories. +func TestGenerateSafeOutputsConfigEmptyRepoMemory(t *testing.T) { + data := &WorkflowData{ + SafeOutputs: &SafeOutputsConfig{}, + RepoMemoryConfig: &RepoMemoryConfig{ + Memories: []RepoMemoryEntry{}, + }, + } + + result := generateSafeOutputsConfig(data) + require.NotEmpty(t, result, "Expected non-empty config") + + var parsed map[string]any + require.NoError(t, json.Unmarshal([]byte(result), &parsed), "Result must be valid JSON") + + _, hasPushRepoMemory := parsed["push_repo_memory"] + assert.False(t, hasPushRepoMemory, "push_repo_memory should not be present when Memories slice is empty") +} diff --git a/pkg/workflow/safe_outputs_generation.go b/pkg/workflow/safe_outputs_generation.go index 1482fd51c3c..4c90f7b7605 100644 --- a/pkg/workflow/safe_outputs_generation.go +++ b/pkg/workflow/safe_outputs_generation.go @@ -599,6 +599,25 @@ func generateSafeOutputsConfig(data *WorkflowData) string { } } + // Add push_repo_memory config if repo-memory is configured + // This enables the push_repo_memory MCP tool for early size validation during agent session + if data.RepoMemoryConfig != nil && len(data.RepoMemoryConfig.Memories) > 0 { + var memories []map[string]any + for _, memory := range data.RepoMemoryConfig.Memories { + memories = append(memories, map[string]any{ + "id": memory.ID, + "dir": "/tmp/gh-aw/repo-memory/" + memory.ID, + "max_file_size": memory.MaxFileSize, + "max_patch_size": memory.MaxPatchSize, + "max_file_count": memory.MaxFileCount, + }) + } + safeOutputsConfig["push_repo_memory"] = map[string]any{ + "memories": memories, + } + safeOutputsConfigLog.Printf("Added push_repo_memory config with %d memory entries", len(memories)) + } + configJSON, _ := json.Marshal(safeOutputsConfig) safeOutputsConfigLog.Printf("Safe outputs config generation complete: %d tool types configured", len(safeOutputsConfig)) return string(configJSON) @@ -1210,6 +1229,12 @@ func generateFilteredToolsJSON(data *WorkflowData, markdownPath string) (string, } // Note: dispatch_workflow tools are generated dynamically below, not from the static tools list + // Add push_repo_memory tool if repo-memory is configured + // This tool enables early size validation during the agent session + if data.RepoMemoryConfig != nil && len(data.RepoMemoryConfig.Memories) > 0 { + enabledTools["push_repo_memory"] = true + } + // Filter tools to only include enabled ones and enhance descriptions var filteredTools []map[string]any for _, tool := range allTools { diff --git a/pkg/workflow/safe_outputs_tools_generation_test.go b/pkg/workflow/safe_outputs_tools_generation_test.go index f31f71b381b..b6defc598f3 100644 --- a/pkg/workflow/safe_outputs_tools_generation_test.go +++ b/pkg/workflow/safe_outputs_tools_generation_test.go @@ -475,3 +475,76 @@ func TestGenerateFilteredToolsJSONSortsCustomJobs(t *testing.T) { assert.Equal(t, "m_job", tools[1]["name"], "Second tool should be m_job") assert.Equal(t, "z_job", tools[2]["name"], "Third tool should be z_job") } + +// TestGenerateFilteredToolsJSONIncludesPushRepoMemoryWithRepoMemoryConfig tests that +// push_repo_memory is included in the filtered tools when RepoMemoryConfig is present. +func TestGenerateFilteredToolsJSONIncludesPushRepoMemoryWithRepoMemoryConfig(t *testing.T) { + data := &WorkflowData{ + SafeOutputs: &SafeOutputsConfig{}, + RepoMemoryConfig: &RepoMemoryConfig{ + Memories: []RepoMemoryEntry{ + {ID: "default", MaxFileSize: 10240, MaxPatchSize: 10240, MaxFileCount: 100}, + }, + }, + } + + result, err := generateFilteredToolsJSON(data, ".github/workflows/test.md") + require.NoError(t, err, "generateFilteredToolsJSON should not error") + + var tools []map[string]any + require.NoError(t, json.Unmarshal([]byte(result), &tools), "Result should be valid JSON") + + toolNames := make(map[string]bool) + for _, tool := range tools { + if name, ok := tool["name"].(string); ok { + toolNames[name] = true + } + } + assert.True(t, toolNames["push_repo_memory"], "push_repo_memory should be present when RepoMemoryConfig is set") +} + +// TestGenerateFilteredToolsJSONExcludesPushRepoMemoryWithoutRepoMemoryConfig tests that +// push_repo_memory is NOT included in the filtered tools when RepoMemoryConfig is absent. +func TestGenerateFilteredToolsJSONExcludesPushRepoMemoryWithoutRepoMemoryConfig(t *testing.T) { + data := &WorkflowData{ + SafeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{}, + }, + RepoMemoryConfig: nil, + } + + result, err := generateFilteredToolsJSON(data, ".github/workflows/test.md") + require.NoError(t, err, "generateFilteredToolsJSON should not error") + + var tools []map[string]any + require.NoError(t, json.Unmarshal([]byte(result), &tools), "Result should be valid JSON") + + for _, tool := range tools { + name, _ := tool["name"].(string) + assert.NotEqual(t, "push_repo_memory", name, "push_repo_memory should NOT be present when RepoMemoryConfig is nil") + } +} + +// TestGenerateFilteredToolsJSONExcludesPushRepoMemoryWithEmptyMemories tests that +// push_repo_memory is NOT included when RepoMemoryConfig has an empty Memories slice. +func TestGenerateFilteredToolsJSONExcludesPushRepoMemoryWithEmptyMemories(t *testing.T) { + data := &WorkflowData{ + SafeOutputs: &SafeOutputsConfig{ + MissingData: &MissingDataConfig{}, + }, + RepoMemoryConfig: &RepoMemoryConfig{ + Memories: []RepoMemoryEntry{}, + }, + } + + result, err := generateFilteredToolsJSON(data, ".github/workflows/test.md") + require.NoError(t, err, "generateFilteredToolsJSON should not error") + + var tools []map[string]any + require.NoError(t, json.Unmarshal([]byte(result), &tools), "Result should be valid JSON") + + for _, tool := range tools { + name, _ := tool["name"].(string) + assert.NotEqual(t, "push_repo_memory", name, "push_repo_memory should NOT be present when Memories is empty") + } +} diff --git a/pkg/workflow/safe_outputs_tools_test.go b/pkg/workflow/safe_outputs_tools_test.go index 03e66a6e40c..70ae2011041 100644 --- a/pkg/workflow/safe_outputs_tools_test.go +++ b/pkg/workflow/safe_outputs_tools_test.go @@ -339,6 +339,7 @@ func TestGetSafeOutputsToolsJSON(t *testing.T) { "create_project", "create_project_status_update", "autofix_code_scanning_alert", + "push_repo_memory", "missing_tool", "missing_data", "noop",