OMP Workbook

Read the source. Follow the evidence.

Prepare a reversible lab

Before adding convenience, make failure inexpensive.

Mara, a seed-library volunteer, wants to experiment without filling her normal project with extension state. She chooses an explicit entry path and a scratch working directory. That gives her a reproducible launch command and a clear place to inspect the session file.

Prerequisites

You need:

  • The workbook-compatible custom omp on PATH.
  • Bun; the recorded checks used Bun 1.3.14.
  • The complete example directory structure, preferably from the ZIP.
  • Matching runtime dependencies where an example imports them.

Check the installed versions, but do not treat a matching version string as proof that an unrelated distribution contains the same custom APIs.

Terminal shell—inspect your installed programs:

Prepare a reversible lab · source excerpt 1; read surrounding instructions
omp --version
bun --version

The Seed Desk stages are complete, independent entries. Load one at a time: all three intentionally own /seeds.

Package Lab’s command-line entries use type-only SDK imports and injected schema builders. Seed Desk stage 3 additionally imports matching @oh-my-pi/omptype and @oh-my-pi/pi-tui runtime packages. Review Desk imports the TUI package through the compatible host’s extension loader.

Do not fix a missing custom package by silently substituting an incompatible upstream package.

Establish the workspace once

Start in the extracted directory that contains seed-desk, review-desk and package-lab.

Terminal shell—create the workbook workspace:

Prepare a reversible lab · source excerpt 2; read surrounding instructions
EXAMPLES="$PWD"
LAB="$(mktemp -d)"
mkdir -p "$LAB/work" "$LAB/agent"
export PI_CODING_AGENT_DIR="$LAB/agent"
export PI_PROFILE=
cd "$LAB/work"

Later commands assume these shell variables remain available.

This is a reversible workspace, not complete isolation:

  • PI_CODING_AGENT_DIR selects the lab’s agent directory.
  • --no-extensions suppresses ambient extension-factory discovery, while explicit entries still load.
  • Other discovery families, environment credentials, package installation and host startup activity have their own behavior.
  • Neither an empty working directory nor --no-session is an OS security boundary.

The recorded isolated scenarios used an additional OS policy denying network access and restricting writes. Ordinary reader launch commands below do not install that policy.

An extension is a factory, not a command-line program

A module loaded with -e exports a default function. OMP calls that function with ExtensionAPI.

Complete TypeScript exercise—save as lab-status.ts in the lab working directory:

Prepare a reversible lab · source excerpt 3; read surrounding instructions
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

export default function labStatus(pi: ExtensionAPI): void {
    pi.setLabel("Workbook lab");

    pi.on("session_start", (_event, ctx) => {
        pi.logger.debug("Workbook lab session started");
        if (ctx.hasUI) {
            ctx.ui.notify("Workbook lab loaded.", "info");
        }
    });
}

Terminal shell—load that exercise:

Prepare a reversible lab · source excerpt 4; read surrounding instructions
omp --no-extensions -e ./lab-status.ts

Expected checkpoint: in a TUI, the session-start handler produces Workbook lab loaded. In a default headless context, the notification is absent; that absence is not a load failure.

Running bun lab-status.ts merely evaluates a module that exports a function. It does not supply OMP’s extension runtime.

Registration first, initialized actions later

The lifecycle has two important phases:

  1. Factory binding: register commands, tools, flags, handlers, renderers and fallback handlers.
  2. Runtime initialization: the host wires session actions and UI, then dispatches events and invocations.

Methods such as sendMessage(), appendEntry(), getAllTools() and setModel() depend on the initialized runtime. Calling them during factory loading raises ExtensionRuntimeNotInitializedError.

An async factory is valid. Seed Desk uses one to read tool.txt before registering a tool. Async initialization does not make runtime actions available early.

Register providers at factory time if needed: their registrations are queued for the model registry. That is a special registration path, not an exception allowing arbitrary session actions during loading.

Keep the lifetimes separate

LifetimeWhat belongs hereWhat does not follow automatically
ProcessLoaded libraries, process-wide registries, explicitly shared busesOne user, one session or one authorization scope
Module evaluationStatic fixtures and helper definitionsFresh mutable state for every SDK session
Factory bindingClosures created when the factory is calledAutomatic reset on /new or session switching
Session/transcriptA session ID, header and journalA global shared database
Current branchThe path from root to the active leafEvery entry in the session file
InvocationThe current handler/tool context and abort signalA context object safe to cache indefinitely

The Field Notes selection later demonstrates the distinction: it survives a transcript change because the factory binding survives.

Trust is already being granted

Source-backed: ExtensionContext.isProjectTrusted() always returns true in this build. It is a compatibility method reflecting that project-local inputs are already trusted by default. It is not a prompt, sandbox or per-directory permission store.

Review the selected module’s entire import graph. Package dependencies and helper modules have the same in-process JavaScript authority as the entry.

Exercise: move pi.sendMessage() into the factory body of a copy of lab-status.ts. What should happen?

Answer: loading should report an uninitialized-runtime error. Move the action into a handler; do not add a delay and hope startup finishes first.

Source, snapshot 2026-08-29: packages/coding-agent/src/extensibility/extensions/loader.ts, getExtensionFactory, ExtensionRuntime, ConcreteExtensionAPI; packages/coding-agent/src/extensibility/extensions/runner.ts, initialize, createContext.

Extensions inside those boundaries · Source chapter: extensions/prepare-a-reversible-lab. Original evidence remains scoped to its recorded snapshot.

Read this chapter as Markdown

Your lesson ticks

A self-reported reading checklist, not proof of real OMP behavior. Only these ticks are saved in this browser. Reading a milestone does not resume, fork, reset or export a session.

Chapters I have worked through
Start here 1
Sessions, resets, and reviewable history 19
Memory and reusable knowledge 14
Tangent work and live control 17
Tool permissions and approvals 15
Extensions inside those boundaries 23
Connections and next steps 8
0 of 97 checked

Checklist saving needs JavaScript and available browser storage.