fix: prevent reflection loop with global cross-instance re-entrant guard#369
Conversation
Previously each plugin instance maintained its own re-entrant guard Map. When the runtime re-loaded the plugin during embedded agent turns (e.g. command:new inside a reflection), a new instance would bypass the guard, causing infinite reflection loops. This change introduces two global guards using Symbol.for + globalThis so ALL plugin instances share the same state: 1. **Global re-entrant lock** — prevents concurrent reflection calls for the same sessionKey across all plugin instances. 2. **Serial loop guard** — imposes a 2-minute cooldown per sessionKey between consecutive reflection runs, preventing gateway-level re-triggering chains (session_end → new session → command:new).
AliceLJY
left a comment
There was a problem hiding this comment.
LGTM — clean, minimal, targeted fix.
Two global guards via Symbol.for + globalThis is the right approach for cross-instance protection. The 2-minute serial cooldown handles the gateway re-triggering chain well.
+41 -0, single file, no dependencies — exactly the kind of surgical fix this problem needs.
@rwmjhb ready for merge.
|
Re-checked this more carefully. The cross-instance Concretely:
That means early-return paths such as:
still arm the 120s cooldown. I reproduced this on
On current So my updated take is:
|
AliceLJY
left a comment
There was a problem hiding this comment.
LGTM — changes are clean, on-topic, and well-tested. Approving.
将 cooldown 时间戳的记录从 finally 无条件执行改为仅当 reflection 通过所有前置条件检查(cfg、session file、conversation)后才记录。 避免因前置条件不满足导致的提前退出误触 cooldown,阻塞后续正常重试。 回应 @rwmjhb 在 CortexReach#369 的审查反馈。
Addressing @rwmjhb's feedback on serial cooldown placementGood catch. The issue was that Fix (8dbdcb5)Added a This means:
The global re-entrant lock cleanup ( |
Problem
Each plugin instance maintained its own re-entrant guard
Map. When the runtime re-loaded the plugin during embedded agent turns (e.g.command:newinside a reflection), a new instance would bypass the guard, causing infinite reflection loops.Solution
Introduce two global guards using
Symbol.for+globalThisso all plugin instances share the same state:sessionKeyacross all plugin instances.sessionKeybetween consecutive reflection runs, preventing gateway-level re-triggering chains (session_end→ new session →command:new).Changes
index.tsmodified — no dependency changes.Symbol.forkeys, safe for multiple loads.SERIAL_GUARD_COOLDOWN_MSconstant (currently 120s).