74 lines
3.3 KiB
Markdown
74 lines
3.3 KiB
Markdown
---
|
|
name: omp-extension-event-probe
|
|
description: "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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```js
|
|
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`.
|