This document details the architecture, configuration, interface, and active enforcement mechanics of the native omp-sequential-thinking extension. By migrating from an external Model Context Protocol (MCP) stdio-based transport layer to native Oh My Pi (OMP) Extension Hooks using the ExtensionAPI, we have achieved zero-latency overhead, in-memory session state preservation, and active cognitive enforcement directly inside the agent loop.
๐ 1. Native Architecture: The ExtensionAPI Paradigm Shift
The native extension replaces the traditional external MCP architecture with a streamlined, hook-based model:
Old MCP Stdio Transport: Relied on spawning an external Node process, translating requests to JSON-RPC over stdin/stdout, and passing them back. This introduced process serialization latency, transport overhead, and lacked a mechanism to block tool calls or inject context dynamically.
New Native OMP Extension Hooks: Dynamically loaded directly inside the host process. Using the OMP ExtensionAPI, the extension hooks directly into the core agent lifecycle, accessing variables in-memory and intercepting events in real-time.
sequenceDiagram
autonumber
participant LLM as AI Agent (LLM)
participant OMP as OMP Core Orchestrator
participant Hooks as Native Extension hooks (ExtensionAPI)
LLM->>OMP: Send user message
Note over OMP,Hooks: "context" hook triggers
Hooks-->>OMP: Inject System Instruction ("Must think N times")
OMP->>LLM: Pass context (system instructions + prompt)
LLM->>OMP: Request tool call (e.g., read_file)
Note over OMP,Hooks: "tool_call" hook intercepts
alt currentThoughts < forcedThoughts
Hooks-->>OMP: Return { block: true, reason: "Must think first" }
OMP->>LLM: Blocked! Please use sequential_thinking
else currentThoughts >= forcedThoughts
OMP->>Hooks: Execute tool call
end
Key Architectural Hooks Leveraged:
pi.registerFlag & pi.getFlag: Registers and retrieves command-line flags.
pi.registerCommand: Adds interactive slash commands for the terminal/TUI.
pi.registerTool: Registers the sequential_thinking tool natively without JSON-RPC overhead.
pi.on("context"): Intercepts the system context before it is delivered to the LLM.
pi.on("turn_start"): Resets runtime state counters when a new user message turn begins.
pi.on("tool_execution_end"): Increments internal thought counters after a thinking step concludes.
pi.on("tool_call"): Blocks any non-thinking tool calls if the cognitive threshold has not been satisfied.
pi.on("message_end"): Dynamically schedules automated follow-up prompt loops if the LLM attempts to yield prematurely.
To load the extension natively without publishing to npm, it is registered under the OMP global plugins directory:
Workspaces Symlinking: The extension directory ~/.omp/extensions/sequential-thinking is linked into ~/.omp/plugins/package.json using local file dependency routing.
Plugin manifest execution:
cd ~/.omp/pluginsbun add file:../extensions/sequential-thinking
This adds "omp-sequential-thinking": "file:../extensions/sequential-thinking" to ~/.omp/plugins/package.json and runs a local bun install to automatically link and cache the extension code.
The sequential_thinking tool parameters are strongly typed and validated at runtime using the standard TypeBox module injected via pi.typebox:
const { Type } = pi.typebox;pi.registerTool({ name: "sequential_thinking", label: "Sequential Thinking", description: "A detailed tool for dynamic and reflective problem-solving through thoughts.", parameters: Type.Object({ thought: Type.String({ description: "Your current thinking step" }), thoughtNumber: Type.Integer({ description: "Current thought number in sequence" }), totalThoughts: Type.Integer({ description: "Estimated total thoughts needed" }), nextThoughtNeeded: Type.Boolean({ description: "Whether another thought step is needed" }), isRevision: Type.Optional(Type.Boolean({ description: "Whether this thought revises a previous one" })), revisesThought: Type.Optional(Type.Integer({ description: "Which thought is being revised" })), branchFromThought: Type.Optional(Type.Integer({ description: "Which thought is the branching point" })), branchId: Type.Optional(Type.String({ description: "Branch identifier" })), needsMoreThoughts: Type.Optional(Type.Boolean({ description: "If more thoughts are needed" })) }), async execute(toolCallId, params) { const nextThoughtNeeded = (forcedThoughts > 0 && params.thoughtNumber < forcedThoughts) ? true : params.nextThoughtNeeded; const statusText = `Thought #${params.thoughtNumber} recorded: "${params.thought}". nextThoughtNeeded: ${nextThoughtNeeded ? "true" : "false"}.`; return { content: [{ type: "text", text: statusText }], details: { thought: params.thought, thoughtNumber: params.thoughtNumber, totalThoughts: params.totalThoughts, nextThoughtNeeded } }; }});
Detailed Parameter Specifications:
๐ญ thought(String, Required): The core qualitative content of the reasoning step.
๐ข thoughtNumber(Integer, Required): The current index of this thought in the timeline (1-based).
๐ totalThoughts(Integer, Required): Estimated number of thoughts required for this sequence. Can change dynamically.
๐ nextThoughtNeeded(Boolean, Required): Acts as the loop driver. If true, indicates another thinking step is scheduled.
โช isRevision(Boolean, Optional): Flag indicating if this step overwrites or corrects an earlier thought.
๐ ๏ธ revisesThought(Integer, Optional): The index of the specific thought being amended or corrected.
๐ branchFromThought(Integer, Optional): For branching logic. Identifies the thought index being forked.
๐ค๏ธ branchId(String, Optional): Unique identifier for the active timeline branch (e.g. "edgecase-handling").
โ needsMoreThoughts(Boolean, Optional): Signifies that the LLM needs to adjust its estimate and request more steps.
Example Payload:
{ "thought": "Let's re-evaluate the symlinking process. Since Bun cached the link, we must run bun install to register it.", "thoughtNumber": 3, "totalThoughts": 5, "nextThoughtNeeded": true, "isRevision": true, "revisesThought": 1}
๐๏ธ 4. Command Line & Interactive Controls
We support both startup configurations and runtime interactive controls to define cognitive thresholds:
1. Startup CLI Flag: --seq-thoughts <N>
Sets the forced thoughts limit directly from the command line on process startup.
Dynamically adjust the cognitive threshold in real-time during an active, interactive session.
Syntax:/seqthink 8
Registration Logic:
pi.registerCommand("seqthink", { description: "Force sequential thinking", handler: async (args) => { forcedThoughts = parseInt(args.trim(), 10) || 0; currentThoughts = 0; // Reset progress for the new target },});
๐ก๏ธ 5. Active Enforcement & Cognitive Interception Mechanics
The extension implements a zero-bypass sandbox enforcing the forced thoughts threshold.
1. System Prompt Context Injection (pi.on("context"))
When forcedThoughts > 0, system instructions are injected to alert the model of its cognitive requirements before the prompt is processed:
pi.on("context", (event) => { if (forcedThoughts > 0) { const devMessage = { role: "developer" as const, content: `You must use the "sequential_thinking" tool at least ${forcedThoughts} times before calling other tools or completing the task.`, timestamp: Date.now(), }; return { messages: [...(event.messages || []), devMessage], }; }});
2. The Sandbox Blocker (pi.on("tool_call"))
If the LLM attempts to execute any other tool (e.g. read, write, bash) before hitting the forcedThoughts limit, the execution is blocked in-flight:
pi.on("tool_call", (event) => { if (forcedThoughts > 0 && currentThoughts < forcedThoughts && event.toolName !== "sequential_thinking") { return { block: true, reason: `Sequential thinking is required. You have recorded ${currentThoughts} thoughts, but need at least ${forcedThoughts} thoughts before calling other tools.`, }; }});
3. The Escape-Prevention Prompt Loop (pi.on("message_end"))
If the LLM tries to complete its message without calling any tools, or tries to output a final response early, the extension intercepts the message end and injects a user prompt loop:
pi.on("message_end", (event) => { if (forcedThoughts > 0 && currentThoughts < forcedThoughts && event.message && event.message.role === "assistant") { const hasThinkingCall = Array.isArray(event.message.content) && event.message.content.some((c: any) => c && c.type === "toolCall" && c.name === "sequential_thinking" ); if (!hasThinkingCall) { pi.sendUserMessage( `You must use the sequential_thinking tool. You have currently recorded ${currentThoughts} thoughts, but need at least ${forcedThoughts} thoughts.`, { deliverAs: "followUp" } ); } }});
๐งช 6. Verification and QA Checklists
To guarantee a flawless deployment, verify the following steps:
Registry Linking Check: Verify "omp-sequential-thinking" is listed in package dependencies.
Tool Registry Verification:
omp --help
Confirm --seq-thoughts is registered under options.
Dynamic Loop Test:
Execute omp --seq-thoughts 3 "Generate a simple calculator in typescript."
Ensure the tool executes exactly 3 times before starting code generation.
Interactive Block Test:
Run omp, type /seqthink 5, then type "Run ls in terminal.".
Verify that the bash tool call is blocked and a prompt warning is displayed.
Deploying to quartz.loca.zone/knowledgebase - Live Premium Reference Resource. ๐