🐛 Bug Report
📋 Pre-flight Checks
📝 Bug Description
The gentle-engram Pi extension (plugin/pi/index.ts) crashes with a stale-ctx error when Pi performs auto-compaction after an agent run.
The session_compact event handler accesses ctx.cwd and ctx.sessionManager.getSessionId() on a context that Pi has already invalidated after replacing the session during auto-compaction. The Pi runtime's ExtensionRunner.assertActive() catches this and throws:
Extension ".../gentle-engram/index.ts" error: This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload().
at ExtensionRunner.assertActive (runner.js:331:19)
at get cwd (runner.js:437:24)
at gentle-engram/index.ts:778:24
at AgentSession._runAutoCompaction
at AgentSession._checkCompaction
at AgentSession._handlePostAgentRun
🔄 Steps to Reproduce
- Install
gentle-engram Pi extension (version 0.1.10)
- Use Pi with the extension loaded and perform enough interaction to trigger auto-compaction
- The extension crashes with the stale-ctx error
Auto-compaction runs automatically after agent responses when the context window is under pressure. The exact trigger is Pi-internal, but it happens consistently during normal use.
✅ Expected Behavior
The session_compact handler should handle the compaction event gracefully without crashing. The extension should continue to function normally after auto-compaction.
❌ Actual Behavior
The extension throws an unhandled error during session_compact because it accesses ctx properties on a stale context. After the crash, engram memory features (mem_save, mem_search, etc.) may become unavailable until Pi is restarted.
🖥️ Environment
Operating System
Linux (Ubuntu/Debian)
Engram Version
0.1.10 (gentle-engram npm package, plugin/pi/)
Agent / Client
OpenCode (Pi coding agent)
💡 Additional Context
Root cause: During auto-compaction, Pi replaces the session internally. The session_compact event fires after the new session is already active, so the ctx parameter passed to the handler is stale. Any access to ctx.cwd, ctx.sessionManager, or any other ctx.* property throws ExtensionRunner.assertActive.
Fix applied locally (in plugin/pi/index.ts):
Three changes:
- Added
currentSessionId module-level variable — caches the active session ID
session_compact handler — no longer accesses ctx. Uses process.cwd() instead of ctx.cwd, and reads from cached currentSessionId instead of calling getSessionId(ctx). The ctx parameter is renamed to _ctx to signal intent.
before_agent_start handler — captures currentSessionId = getSessionId(ctx) right after initOnce, since this handler receives a fresh, valid ctx.
Key insight: process.cwd() is safe because the process working directory does not change during the session lifecycle.
Full diff of the fix:
+ let currentSessionId: string | undefined;
- pi.on("session_compact", async (event, ctx) => {
- await initOnce(ctx.cwd);
- const sessionId = getSessionId(ctx);
+ pi.on("session_compact", async (event, _ctx) => {
+ // session_compact fires during auto-compaction, after Pi has already
+ // replaced the session — the passed ctx is stale. Do NOT access it.
+ await initOnce(process.cwd());
+ const sessionId = currentSessionId;
pi.on("before_agent_start", async (event, ctx) => {
await initOnce(ctx.cwd);
- const sessionId = getSessionId(ctx);
+ currentSessionId = getSessionId(ctx);
+ const sessionId = currentSessionId;
File path in repo: plugin/pi/index.ts (published as gentle-engram on npm).
Note: other handlers (session_shutdown, before_tool_call, etc.) also access ctx — those are safe because they fire while the session is still active. Only session_compact is affected because it fires during the session replacement window.
🐛 Bug Report
📋 Pre-flight Checks
status:approvedbefore a PR can be opened📝 Bug Description
The
gentle-engramPi extension (plugin/pi/index.ts) crashes with a stale-ctx error when Pi performs auto-compaction after an agent run.The
session_compactevent handler accessesctx.cwdandctx.sessionManager.getSessionId()on a context that Pi has already invalidated after replacing the session during auto-compaction. The Pi runtime'sExtensionRunner.assertActive()catches this and throws:🔄 Steps to Reproduce
gentle-engramPi extension (version 0.1.10)Auto-compaction runs automatically after agent responses when the context window is under pressure. The exact trigger is Pi-internal, but it happens consistently during normal use.
✅ Expected Behavior
The
session_compacthandler should handle the compaction event gracefully without crashing. The extension should continue to function normally after auto-compaction.❌ Actual Behavior
The extension throws an unhandled error during
session_compactbecause it accessesctxproperties on a stale context. After the crash, engram memory features (mem_save, mem_search, etc.) may become unavailable until Pi is restarted.🖥️ Environment
Operating System
Linux (Ubuntu/Debian)
Engram Version
0.1.10 (gentle-engram npm package,
plugin/pi/)Agent / Client
OpenCode (Pi coding agent)
💡 Additional Context
Root cause: During auto-compaction, Pi replaces the session internally. The
session_compactevent fires after the new session is already active, so thectxparameter passed to the handler is stale. Any access toctx.cwd,ctx.sessionManager, or any otherctx.*property throwsExtensionRunner.assertActive.Fix applied locally (in
plugin/pi/index.ts):Three changes:
currentSessionIdmodule-level variable — caches the active session IDsession_compacthandler — no longer accessesctx. Usesprocess.cwd()instead ofctx.cwd, and reads from cachedcurrentSessionIdinstead of callinggetSessionId(ctx). Thectxparameter is renamed to_ctxto signal intent.before_agent_starthandler — capturescurrentSessionId = getSessionId(ctx)right afterinitOnce, since this handler receives a fresh, valid ctx.Key insight:
process.cwd()is safe because the process working directory does not change during the session lifecycle.Full diff of the fix:
File path in repo:
plugin/pi/index.ts(published asgentle-engramon npm).Note: other handlers (
session_shutdown,before_tool_call, etc.) also accessctx— those are safe because they fire while the session is still active. Onlysession_compactis affected because it fires during the session replacement window.