Pi Extension Session Persistence and Compaction Re-injection Process Explained

The i-have-adhd Pi extension persists ADHD mode state across session lifecycles by appending custom session entries and automatically re-injects ruleset markers after compaction events to maintain consistent model behavior.

The ayghri/i-have-adhd repository implements a Pi extension that must maintain a consistent ruleset presence throughout a conversation session. Because Pi compacts session trees to manage context window limits, the extension implements a robust persistence mechanism that survives restarts, tree rebuilds, and compaction cycles.

Persisting State with Custom Session Entries

The extension saves the enabled/disabled state as a durable session entry using the pi.appendEntry API. When a user toggles ADHD mode via the /i-have-adhd on command, the setEnabled function serializes the state to the session tree.

In extensions/i-have-adhd.ts (lines 57-58), the implementation appends an entry of type i-have-adhd-state:

function setEnabled(nextEnabled: boolean, ctx: ExtensionContext): void {
  enabled = nextEnabled;
  pi.appendEntry(STATE_ENTRY_TYPE, { enabled } satisfies AdhdModeState);
  updateStatus(ctx);
  syncContext(ctx);
}

This custom session entry survives across session restarts because it becomes part of the persistent session tree structure, unlike transient in-memory variables.

Restoring State Across Session Lifecycle Events

To recover the saved state after interruptions or rebuilds, the extension registers handlers for Pi's lifecycle events. The implementation listens for both session_start and session_tree events in extensions/i-have-adhd.ts (lines 219-220):

pi.on("session_start", async (_event, ctx) => restoreState(ctx));
pi.on("session_tree",  async (_event, ctx) => restoreState(ctx));

The restoreState function delegates to getSavedState (lines 66-78), which queries the session tree for the most recent i-have-adhd-state entry. This ensures that session persistence maintains user preferences even when the Pi client reloads or reconstructs the conversation tree from storage.

Detecting Ruleset Presence in Context

Before injecting or re-injecting content, the extension must determine whether the ruleset currently exists in the model's context window. The helper rulesAreInContext (lines 90-96 of extensions/i-have-adhd.ts) utilizes contextMessages from extensions/context-compat.ts (lines 12-34) to fetch current context entries.

The detection logic relies on latestMarkerIsActive (lines 41-60), which iterates through context messages to identify the most recent custom marker:

export function latestMarkerIsActive(
  messages: readonly ContextMessageMarker[],
  activeType: string,
  disabledType: string,
): boolean {
  let active = false;
  for (const message of messages) {
    if (message.role !== "custom" && message.type !== "custom_message") continue;
    if (message.customType === activeType) active = true;
    else if (message.customType === disabledType) active = false;
  }
  return active;
}

This function returns true only when the last relevant custom message contains the rules injection (i-have-adhd-rules) rather than a disabled notice (i-have-adhd-disabled).

Re-injecting Rules After Session Compaction

Pi periodically compacts session trees by summarizing or dropping older entries to manage token limits. The extension handles compaction re-injection by listening for the session_compact event in extensions/i-have-adhd.ts (line 221):

pi.on("session_compact", async (_event, ctx) => syncContext(ctx));

The syncContext function implements the core re-injection logic. It checks rulesAreInContext to verify whether the ruleset marker survived the compaction. If ADHD mode is enabled but the marker is missing, the function sends a new custom message containing the full ruleset (lines 21-30). Conversely, if the mode is disabled but a stale rules marker remains, it sends a disabled notice to clear the context (lines 33-40).

function syncContext(ctx: ExtensionContext): void {
  const injected = rulesAreInContext(ctx);
  if (enabled && !injected) {
    pi.sendMessage(
      { customType: RULES_MESSAGE_TYPE, content: `${RULES_HEADER}\n\n${rules}` },
      { triggerTurn: false },
    );
  }
  // Handles the disabled case to prevent stale rule application
}

This guarantees that the model always receives the correct behavioral instructions regardless of compaction events, without duplicating the ruleset on every turn.

Summary

  • Custom session entries store the ADHD mode state via pi.appendEntry with type i-have-adhd-state, ensuring persistence across restarts.
  • Lifecycle event handlers for session_start and session_tree trigger restoreState to rehydrate preferences when sessions initialize or rebuild.
  • Context detection through latestMarkerIsActive determines whether the ruleset currently exists in the model's working context.
  • Compaction resilience via the session_compact listener ensures rules are automatically re-injected when tree summarization removes them, using the syncContext reconciliation logic.

Frequently Asked Questions

How does the Pi extension maintain state after a browser refresh?

The extension persists the enabled/disabled flag as a custom session entry using pi.appendEntry with the type i-have-adhd-state. When the session restarts, the session_start event triggers restoreState, which reads the most recent state entry from the session tree and reapplies the user's preference, maintaining continuity across page reloads.

What triggers the ruleset re-injection mechanism?

The session_compact event triggers the re-injection check. When Pi compacts the conversation tree—removing or summarizing older entries to conserve context window space—the extension receives this event and executes syncContext. If the compaction removed the ruleset marker while ADHD mode remains enabled, the function automatically sends a fresh ruleset message to restore the intended behavior.

Why distinguish between i-have-adhd-rules and i-have-adhd-disabled markers?

The extension uses distinct custom message types to handle state transitions precisely. When ADHD mode is disabled, sending an i-have-adhd-disabled marker explicitly overrides any previous ruleset in the context. This prevents the model from continuing to apply ADHD formatting instructions after the user has turned off the feature, ensuring clean state transitions without requiring full context clearing.

Where does the extension check if the ruleset is currently active?

The rulesAreInContext function in extensions/i-have-adhd.ts (lines 90-96) performs this check by calling contextMessages from extensions/context-compat.ts. It then passes the results to latestMarkerIsActive to determine whether the most recent relevant custom message contains active rules or a disabled notice, preventing duplicate injections.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →