Skip to content

Commit cda8351

Browse files
Copilotpelikhan
andauthored
Harden comment-memory file extraction and scan behavior
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/8f6cfa44-79b4-46b5-96ab-081eb8dcc7b7 Co-authored-by: pelikhan <[email protected]>
1 parent 11a9c42 commit cda8351

2 files changed

Lines changed: 66 additions & 10 deletions

File tree

actions/setup/js/safe_output_handler_manager.cjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,8 @@ async function processMessages(messageHandlers, messages, onItemCreated = null)
816816
* @param {string} messageType - Type of the message
817817
* @param {any} message - The message object
818818
* @param {any} [result] - Handler result (used for transformed/managed bodies)
819+
* For comment_memory, handlers return a managedBody that includes XML wrapper/footer;
820+
* this differs from message.body and must be used for temporary ID detection.
819821
* @returns {string|null} Content to check for temporary IDs
820822
*/
821823
function getContentToCheck(messageType, message, result) {

actions/setup/js/setup_comment_memory_files.cjs

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,58 @@ const PROMPT_PATH = "/tmp/gh-aw/aw-prompts/prompt.txt";
1212
const PROMPT_START_MARKER = "<!-- gh-aw-comment-memory-prompt:start -->";
1313
const PROMPT_END_MARKER = "<!-- gh-aw-comment-memory-prompt:end -->";
1414
const MAX_SCAN_PAGES = 50;
15+
const MAX_SCAN_EMPTY_PAGES = 5;
16+
const MAX_MEMORY_ID_LENGTH = 64;
17+
18+
function isSafeMemoryId(memoryId) {
19+
if (typeof memoryId !== "string" || memoryId.length === 0 || memoryId.length > MAX_MEMORY_ID_LENGTH) {
20+
return false;
21+
}
22+
if (memoryId.includes("..") || memoryId.includes("/") || memoryId.includes("\\")) {
23+
return false;
24+
}
25+
return /^[A-Za-z0-9_-]+$/.test(memoryId);
26+
}
1527

1628
function extractCommentMemoryEntries(commentBody) {
1729
if (!commentBody || typeof commentBody !== "string") {
1830
return [];
1931
}
2032

21-
const pattern = new RegExp(`<${COMMENT_MEMORY_TAG}\\s+id="([A-Za-z0-9_-]+)">([\\s\\S]*?)<\\/${COMMENT_MEMORY_TAG}>`, "g");
22-
const matches = [];
23-
let match = pattern.exec(commentBody);
24-
while (match) {
25-
matches.push({
26-
memoryId: match[1],
27-
content: String(match[2] || "").trim(),
28-
});
29-
match = pattern.exec(commentBody);
33+
const entries = [];
34+
const closeTag = `</${COMMENT_MEMORY_TAG}>`;
35+
let cursor = 0;
36+
while (cursor < commentBody.length) {
37+
const openStart = commentBody.indexOf(`<${COMMENT_MEMORY_TAG} id="`, cursor);
38+
if (openStart < 0) {
39+
break;
40+
}
41+
42+
const idStart = openStart + `<${COMMENT_MEMORY_TAG} id="`.length;
43+
const idEnd = commentBody.indexOf('">', idStart);
44+
if (idEnd < 0) {
45+
break;
46+
}
47+
48+
const memoryId = commentBody.slice(idStart, idEnd);
49+
const contentStart = idEnd + 2;
50+
const closeStart = commentBody.indexOf(closeTag, contentStart);
51+
if (closeStart < 0) {
52+
break;
53+
}
54+
55+
if (isSafeMemoryId(memoryId)) {
56+
entries.push({
57+
memoryId,
58+
content: String(commentBody.slice(contentStart, closeStart) || "").trim(),
59+
});
60+
} else {
61+
core.warning(`comment_memory setup: skipping unsafe memory_id '${memoryId}' found in managed comment`);
62+
}
63+
64+
cursor = closeStart + closeTag.length;
3065
}
31-
return matches;
66+
return entries;
3267
}
3368

3469
function loadSafeOutputsConfig() {
@@ -90,6 +125,7 @@ async function collectCommentMemoryFiles(githubClient, commentMemoryConfig) {
90125

91126
core.info(`comment_memory setup: loading managed comment memory from ${targetRepo.slug}#${targetNumber}`);
92127
const memoryMap = new Map();
128+
let emptyPageCount = 0;
93129

94130
for (let page = 1; page <= MAX_SCAN_PAGES; page++) {
95131
const { data } = await githubClient.rest.issues.listComments({
@@ -104,13 +140,27 @@ async function collectCommentMemoryFiles(githubClient, commentMemoryConfig) {
104140
break;
105141
}
106142

143+
let pageAddedEntries = 0;
107144
for (const comment of data) {
108145
const entries = extractCommentMemoryEntries(comment.body);
109146
for (const entry of entries) {
147+
if (!memoryMap.has(entry.memoryId) || memoryMap.get(entry.memoryId) !== entry.content) {
148+
pageAddedEntries++;
149+
}
110150
memoryMap.set(entry.memoryId, entry.content);
111151
}
112152
}
113153

154+
if (pageAddedEntries === 0) {
155+
emptyPageCount++;
156+
if (emptyPageCount >= MAX_SCAN_EMPTY_PAGES) {
157+
core.info(`comment_memory setup: stopping scan after ${emptyPageCount} pages without new memory entries`);
158+
break;
159+
}
160+
} else {
161+
emptyPageCount = 0;
162+
}
163+
114164
if (data.length < 100) {
115165
break;
116166
}
@@ -119,6 +169,10 @@ async function collectCommentMemoryFiles(githubClient, commentMemoryConfig) {
119169
fs.mkdirSync(COMMENT_MEMORY_DIR, { recursive: true });
120170
const writtenFiles = [];
121171
for (const [memoryId, content] of memoryMap.entries()) {
172+
if (!isSafeMemoryId(memoryId)) {
173+
core.warning(`comment_memory setup: skipping unsafe memory_id '${memoryId}' while writing files`);
174+
continue;
175+
}
122176
const filePath = path.join(COMMENT_MEMORY_DIR, `${memoryId}.md`);
123177
fs.writeFileSync(filePath, `${content}\n`);
124178
writtenFiles.push(filePath);

0 commit comments

Comments
 (0)