Files
command-center/agent/managed-skills/omp-extension-event-probe/SKILL.md
T

3.3 KiB

name, description
name description
omp-extension-event-probe Prove which lifecycle events the omp harness actually dispatches to extensions, with a throwaway timestamping probe plugin, instead of inferring delivery from side effects.

Prove harness→extension event delivery with a probe plugin

Use when an extension handler seems never to fire, or when you must know when an event arrives relative to another (mid-run vs settle). Never infer dispatch from the feature's side effects: async work in the handler (LLM rewrites, synthesis, network) lags seconds and destroys the timing signal.

Shape (no build step needed)

/tmp/probe/package.json — the installer validates the declared entry on disk:

{
  "name": "event-probe",
  "version": "0.0.0",
  "type": "module",
  "exports": "./index.js",
  "pi": { "extensions": ["./index.js"] }
}

/tmp/probe/index.js — append arrival times, not counts:

import { appendFileSync } from "node:fs";
const LOG = "/tmp/probe.log";
const stamp = (line) => appendFileSync(LOG, `${new Date().toISOString().slice(11, 23)} ${line}\n`);
export default function probe(pi) {
  pi.on("agent_start", () => stamp("agent_start"));
  pi.on("turn_end", (e) => stamp(`turn_end turn=${e.turnIndex} toolResults=${(e.toolResults ?? []).length}`));
  pi.on("agent_end", (e) => stamp(`agent_end messages=${(e.messages ?? []).length} willContinue=${String(e.willContinue)}`));
}

Run

  1. cd ~/.omp/plugins && omp plugin link /tmp/probe (link, never edit node_modules), then omp plugin doctor → expect a ✔ plugin:event-probe line.
  2. Launch a fresh TUI session in a pty (omp --cwd /tmp/probe-run, pty: true). Extensions load at startup only — an already-running session never sees the probe.
  3. PTY gotcha: input sent before the splash clears is swallowed. Read the screen first, then submit text and finish with a separate write containing a bare \r (LF alone left the text sitting in the composer).
  4. Drive a task with ≥2 tool rounds so turns and settles separate in time (sequential reads work and need no approval; ask for steps "strictly one after the other" or the model parallelises them into one turn).
  5. Wait for a terminal line with an async bounded loop (grep -q agent_end, sleep 1 inside one backgrounded bash call) — never poll across turns.

Read

turn_end timestamps strictly earlier than agent_end ⇒ per-turn delivery works. willContinue=true on agent_end marks a non-terminal settle (harness maps isTerminal === false to it) — handlers that return on it never speak.

Teardown (mandatory)

omp plugin uninstall event-probe; omp plugin doctor; delete /tmp/probe*. The link writes into ~/.omp/plugins/package.json (tracked, live tier) — uninstall restores it; confirm with grep for the probe name returning nothing.

Ground truth read for free

Event union + payloads: ~/.omp/plugins/node_modules/@oh-my-pi/pi-coding-agent/dist/types/extensibility/shared-events.d.ts; the session→extension mapping lives in src/extensibility/extensions/lifecycle-mirror.ts (extensionEventFromSessionEvent). Extension-level turn_end carries turnIndex/message/toolResults but no willContinue — that field exists only on agent_end, so "is this the last turn" cannot be decided from turn_end.