From 9a0121250b16897c379d9db6ebae1149dbe78099 Mon Sep 17 00:00:00 2001 From: ProofOfConcept Date: Tue, 24 Mar 2026 12:27:31 -0400 Subject: [PATCH] agents: fix resolve_placeholders infinite loop The placeholder resolver re-scanned from the beginning of the string after each expansion. If expanded node content contained {{...}} patterns (which core-personality does), those got expanded recursively. Cyclic node references caused infinite string growth. Fix: track a position offset that advances past each substitution, so expanded content is never re-scanned for placeholders. Co-Authored-By: Claude Opus 4.6 (1M context) --- poc-memory/src/agents/defs.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/poc-memory/src/agents/defs.rs b/poc-memory/src/agents/defs.rs index d8d7fe9..3431b57 100644 --- a/poc-memory/src/agents/defs.rs +++ b/poc-memory/src/agents/defs.rs @@ -611,19 +611,25 @@ pub fn resolve_placeholders( ) -> (String, Vec) { let mut result = template.to_string(); let mut extra_keys = Vec::new(); + let mut pos = 0; loop { - let Some(start) = result.find("{{") else { break }; - let Some(end) = result[start + 2..].find("}}") else { break }; - let end = start + 2 + end; + let Some(rel_start) = result[pos..].find("{{") else { break }; + let start = pos + rel_start; + let Some(rel_end) = result[start + 2..].find("}}") else { break }; + let end = start + 2 + rel_end; let name = result[start + 2..end].trim().to_lowercase(); match resolve(&name, store, graph, keys, count) { Some(resolved) => { + let len = resolved.text.len(); extra_keys.extend(resolved.keys); result.replace_range(start..end + 2, &resolved.text); + pos = start + len; } None => { let msg = format!("(unknown: {})", name); + let len = msg.len(); result.replace_range(start..end + 2, &msg); + pos = start + len; } } }