Short description
In-process @gotgenes/pi-subagents children inherit and initialize Magic Context. Four parallel children can trigger concurrent global Pi session scans and crash the parent Node process with heap OOM.
What happened?
Environment where this reproduced:
- Magic Context:
@cortexkit/pi-magic-context 0.32.4
- Pi:
@earendil-works/pi-coding-agent 0.81.1
- Subagents:
@gotgenes/pi-subagents 18.1.0
- Node: 22.23.0
- Platform: macOS arm64
- Pi session store: about 392 JSONL sessions
Reproduction shape:
- Register both
npm:@gotgenes/pi-subagents and npm:@cortexkit/pi-magic-context in Pi user packages.
- Start a normal persisted Pi session and let Magic Context finish its boot-quiet period.
- Start four background
@gotgenes/pi-subagents children in parallel. Each child only needs one MCP tool call.
- The parent Pi process terminates with
SIGABRT / JavaScript heap OOM.
Observed failure, sanitized:
Mark-Compact 4010.4 (4352.5) -> 3255.4 (4333.2) MB ... allocation failure
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
node::OOMErrorHandler
v8::internal::V8::FatalProcessOutOfMemory
v8::internal::JsonParser<...>::ParseJson
Builtin_JsonParse
node::fs::FSReqCallback::Resolve
This reproduced repeatedly with the OOM stack in JSON parsing during an async filesystem callback. Running without Magic Context removes this parallel-start crash path; Pi native compaction remains usable.
Why this appears different from #238
#238 covers Magic Context's own subprocess-based historian/dreamer children. Those children use MAGIC_CONTEXT_PI_SUBAGENT=1, and v0.33.0 adds isolated retry / extension controls for that runner.
@gotgenes/pi-subagents 18.x uses in-process createAgentSession() children instead:
- its child loader intentionally inherits all parent extensions;
- it emits
subagents:child:session-created immediately before bindExtensions();
- it does not set
MAGIC_CONTEXT_PI_SUBAGENT=1 because process.env is process-global and unsafe as a per-child marker.
Magic Context's current Pi entry guard only checks MAGIC_CONTEXT_PI_SUBAGENT=1, so it does not recognize this child type. Each child can initialize Magic Context and reach Pi session discovery/backfill paths using SessionManager.listAll().
Pi's SessionManager.listAll() compounds the problem: session metadata search retains text from all user/assistant messages, and each call runs multiple concurrent file readers. Several in-process callers multiply peak memory.
I checked the v0.33.0 release and source. Its smaller lazy-loaded bundle reduces startup cost, but I did not find integration with @gotgenes/pi-subagents lifecycle events or a guard for external in-process child sessions. I have not claimed a runtime reproduction on 0.33.0; the confirmed crashes were on 0.32.4.
Expected behavior
Magic Context should not perform full extension initialization or global session backfill inside ephemeral external in-process subagent sessions.
Possible fix directions:
- support a process-safe child-session marker from Pi session metadata or a documented lifecycle integration;
- recognize
@gotgenes/pi-subagents child session lifecycle without relying on a process-global environment variable;
- at minimum, guard global
SessionManager.listAll() / backfill work so child runtimes cannot fan it out concurrently;
- add a regression test with multiple in-process child sessions binding extensions in parallel.
A complementary fix belongs in Pi core to bound SessionManager.listAll() memory, but skipping unnecessary Magic Context initialization in child sessions removes this specific multiplier.
Diagnostics
- Client: Pi
- Confirmed plugin version: 0.32.4
- Latest version inspected: 0.33.0
- Pi version: 0.81.1
- @gotgenes/pi-subagents: 18.1.0
- Node: 22.23.0
- Platform: macOS arm64
- Crash: SIGABRT, V8 heap OOM around 4.35 GB
- Workaround: uninstall `npm:@cortexkit/pi-magic-context` and use Pi native compaction
Plugin version
0.32.4
OpenCode version
N/A
Platform
macOS arm64
Client
Pi
Log output (optional)
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
Short description
In-process
@gotgenes/pi-subagentschildren inherit and initialize Magic Context. Four parallel children can trigger concurrent global Pi session scans and crash the parent Node process with heap OOM.What happened?
Environment where this reproduced:
@cortexkit/pi-magic-context0.32.4@earendil-works/pi-coding-agent0.81.1@gotgenes/pi-subagents18.1.0Reproduction shape:
npm:@gotgenes/pi-subagentsandnpm:@cortexkit/pi-magic-contextin Pi user packages.@gotgenes/pi-subagentschildren in parallel. Each child only needs one MCP tool call.SIGABRT/ JavaScript heap OOM.Observed failure, sanitized:
This reproduced repeatedly with the OOM stack in JSON parsing during an async filesystem callback. Running without Magic Context removes this parallel-start crash path; Pi native compaction remains usable.
Why this appears different from #238
#238 covers Magic Context's own subprocess-based historian/dreamer children. Those children use
MAGIC_CONTEXT_PI_SUBAGENT=1, and v0.33.0 adds isolated retry / extension controls for that runner.@gotgenes/pi-subagents18.x uses in-processcreateAgentSession()children instead:subagents:child:session-createdimmediately beforebindExtensions();MAGIC_CONTEXT_PI_SUBAGENT=1becauseprocess.envis process-global and unsafe as a per-child marker.Magic Context's current Pi entry guard only checks
MAGIC_CONTEXT_PI_SUBAGENT=1, so it does not recognize this child type. Each child can initialize Magic Context and reach Pi session discovery/backfill paths usingSessionManager.listAll().Pi's
SessionManager.listAll()compounds the problem: session metadata search retains text from all user/assistant messages, and each call runs multiple concurrent file readers. Several in-process callers multiply peak memory.I checked the v0.33.0 release and source. Its smaller lazy-loaded bundle reduces startup cost, but I did not find integration with
@gotgenes/pi-subagentslifecycle events or a guard for external in-process child sessions. I have not claimed a runtime reproduction on 0.33.0; the confirmed crashes were on 0.32.4.Expected behavior
Magic Context should not perform full extension initialization or global session backfill inside ephemeral external in-process subagent sessions.
Possible fix directions:
@gotgenes/pi-subagentschild session lifecycle without relying on a process-global environment variable;SessionManager.listAll()/ backfill work so child runtimes cannot fan it out concurrently;A complementary fix belongs in Pi core to bound
SessionManager.listAll()memory, but skipping unnecessary Magic Context initialization in child sessions removes this specific multiplier.Diagnostics
Plugin version
0.32.4
OpenCode version
N/A
Platform
macOS arm64
Client
Pi
Log output (optional)