How the i-have-adhd Extension Tracks Context with latestMarkerIsActive in Pi

The i-have-adhd extension maintains ADHD-friendly formatting rules by scanning conversation history with the latestMarkerIsActive utility to verify whether the most recent context marker indicates an active or disabled state.

The i-have-adhd extension for the Pi AI assistant ensures that specialized formatting guidelines remain synchronized with the model's context throughout extended conversations. According to the ayghri/i-have-adhd source code, the extension tracks whether its ruleset is currently injected by analyzing custom message markers in the session history using a dedicated compatibility layer. This tracking mechanism relies on the latestMarkerIsActive function defined in extensions/context-compat.ts to determine the current state of ADHD accommodation rules.

The latestMarkerIsActive Utility

The core logic for context tracking resides in the latestMarkerIsActive function within extensions/context-compat.ts (lines 41-61). This utility determines whether a specific marker type represents the current active state by performing a chronological scan of conversation messages.

The function accepts three parameters: the array of context messages, the marker type representing the enabled state (RULES_MESSAGE_TYPE), and the marker type representing the disabled state (DISABLED_MESSAGE_TYPE). It iterates through messages sequentially, setting an internal flag to true when encountering the rules marker and flipping it to false when encountering the disabled marker. After processing the entire array, the final boolean value indicates whether the rules are currently active based on the most recent relevant marker in the history.

Collecting Context Messages

Before scanning can occur, the extension must safely extract the conversation history from Pi's session manager. The contextMessages function in context-compat.ts (lines 12-39) provides this abstraction.

This utility attempts to read messages through multiple API surface areas, checking for either buildSessionContext().messages or buildContextEntries() methods on the session manager object. If these methods are unavailable or throw exceptions, the function gracefully returns an empty array rather than crashing. This defensive programming ensures the extension remains compatible across different Pi versions while providing a standardized ContextMessageMarker array for downstream processing.

Scanning for the Newest Marker

Once the message array is retrieved, the extension determines the current rules state through a linear chronological scan. The implementation in latestMarkerIsActive (lines 48-58) processes each message individually.

When the scan encounters a message whose customType property matches RULES_MESSAGE_TYPE, it sets a local active variable to true. If it subsequently encounters a message with customType matching DISABLED_MESSAGE_TYPE, it updates the variable to false. This simple state machine approach ensures that only the most recent marker of either type influences the final result, effectively ignoring older markers that may have been superseded by newer state changes.

Synchronizing Context State

The main extension file extensions/i-have-adhd.ts orchestrates state synchronization through the rulesAreInContext and syncContext functions (lines 34-59). The extension triggers this synchronization on critical lifecycle events: session_start, session_tree, and session_compact (lines 37-40).

Using the boolean result from latestMarkerIsActive, the extension decides whether to inject or withdraw rules:

  • When enabled but inactive: The extension calls pi.sendMessage with customType: RULES_MESSAGE_TYPE to inject the ADHD formatting guidelines.
  • When disabled but active: The extension sends a message with customType: DISABLED_MESSAGE_TYPE to explicitly mark the rules as withdrawn.

This bidirectional synchronization ensures the context always reflects the user's current preference regarding ADHD accommodations.

Why the Latest Marker Matters

Conversation contexts in Pi undergo periodic compaction to remove older entries and manage memory usage. During session_compact events, earlier messages—including previously injected rules—may be dropped from the active context.

By always evaluating the latest marker rather than counting total occurrences, the extension guarantees that stale rulesets are never left orphaned in a compacted context without a corresponding disabled marker. The linear scan approach is computationally inexpensive because conversation contexts typically remain small, making this check efficient enough to run on every relevant lifecycle event.

Implementation Example

The following TypeScript implementation demonstrates how to verify whether ADHD rules are currently active using the utilities from extensions/context-compat.ts. This pattern mirrors the logic used internally by the extension to determine when to inject or withdraw formatting guidelines.

import { contextMessages, latestMarkerIsActive } from "./extensions/context-compat";

const RULES_MESSAGE_TYPE = "i-have-adhd-rules";
const DISABLED_MESSAGE_TYPE = "i-have-adhd-disabled";

function areRulesActive(sessionManager: unknown): boolean {
  const messages = contextMessages(sessionManager);
  return latestMarkerIsActive(
    messages,
    RULES_MESSAGE_TYPE,
    DISABLED_MESSAGE_TYPE
  );
}

The extension's internal synchronization logic uses this pattern within syncContext to maintain state:

function syncContext(ctx: ExtensionContext) {
  const rulesActive = areRulesActive(ctx.sessionManager);
  
  if (enabled && !rulesActive) {
    pi.sendMessage({ 
      customType: "i-have-adhd-rules", 
      content: RULES_HEADER + "\n\n" + rules, 
      display: false 
    });
  } else if (!enabled && rulesActive) {
    pi.sendMessage({ 
      customType: "i-have-adhd-disabled", 
      content: DISABLED_NOTICE, 
      display: false 
    });
  }
}

Summary

  • The latestMarkerIsActive function in extensions/context-compat.ts performs a chronological scan of conversation messages to determine whether ADHD rules are currently injected.
  • contextMessages provides defensive abstraction over Pi's session manager API, returning empty arrays on failure to prevent crashes.
  • The scan logic treats RULES_MESSAGE_TYPE as activating and DISABLED_MESSAGE_TYPE as deactivating, with the final marker in the sequence determining the current state.
  • Synchronization occurs on session_start, session_tree, and session_compact events to maintain consistency across conversation lifecycle changes.
  • Tracking the latest marker specifically prevents state desynchronization when older context entries are removed during compaction.

Frequently Asked Questions

How does latestMarkerIsActive handle missing or corrupted session data?

The contextMessages wrapper function ensures resilience by attempting multiple API methods (buildSessionContext().messages and buildContextEntries()) and catching any thrown exceptions. If the session manager is unavailable or returns unexpected structures, contextMessages returns an empty array, causing latestMarkerIsActive to return false by default rather than throwing errors.

What happens when both marker types appear in the conversation history?

When both RULES_MESSAGE_TYPE and DISABLED_MESSAGE_TYPE markers exist in the context, the chronological order determines the outcome. The function processes messages sequentially, updating an internal boolean flag each time it encounters either marker type. Only the final occurrence of either marker influences the return value, effectively canceling out any previous state changes.

Why scan the entire message array instead of checking only the last message?

While the function could theoretically check only the final message, scanning the entire array ensures compatibility with conversation compaction and tree restructuring events where messages might be reordered or intermediate markers added. The linear scan from extensions/context-compat.ts (lines 48-58) is computationally trivial for typical context sizes and guarantees accurate state detection regardless of message ordering complexities.

When exactly does the extension inject or withdraw the ADHD rules?

The extension triggers synchronization via syncContext on three specific Pi lifecycle events: session_start (new conversation), session_tree (conversation branching), and session_compact (context cleanup). During each trigger, if latestMarkerIsActive returns false but the rules are enabled in settings, the extension injects them; conversely, if it returns true but rules are disabled, it sends a withdrawal marker.

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 →