← Examples

Codex agent provider

The official Codex extension is a TypeScript agent provider. It observes a running Codex CLI process and its JSONL rollout journal; it never starts Codex or reads an unrelated local home.

What it demonstrates

  • A manifest with agent-observation and a single agentProviders contribution.
  • Foreground-process matching by executable name.
  • Binding a session only to a writable rollout owned by that terminal’s process tree.
  • Watching the root journal, discovering Codex child journals, and mapping records into agent events.

Package shape

Shell
terminay-agent-codex/
├── package.json
├── src/
   ├── constants.ts
   ├── index.ts
   └── provider.ts
├── fixtures/v0.1/basic.jsonl
└── test/

src/index.ts activates the extension. src/provider.ts contains the process and file observation logic; fixtures and tests keep the journal mapping compatible.

Register the provider

TypeScript
import { defineExtension } from "@terminay/extension-api";
import { PROVIDER_ID } from "./constants.js";
import { codexAgentProvider } from "./provider.js";

export default defineExtension({
  activate(context) {
    context.subscriptions.add(
      context.agents.registerProvider(PROVIDER_ID, codexAgentProvider),
    );
  },
});

The contribution ID in the package manifest is the same PROVIDER_ID passed to registerProvider(). Adding the registration to subscriptions disposes it when the extension is deactivated.

Observe and bind safely

TypeScript
export const codexAgentProvider = defineAgentProvider({
  mappingVersion: MAPPING_VERSION,

  matchesForeground(process) {
    return isCodexForeground(process.executableName);
  },

  async observe(terminal) {
    if (!terminal.capabilities.has("process-observation")
      || !terminal.capabilities.has("filesystem-observation")
      || !terminal.capabilities.has("agent-journal")) {
      return { state: "unavailable", reason: "environment-capability-missing" };
    }

    const rollout = await findProcessBoundRootRollout(terminal);
    if (!rollout) return { state: "not-bound" };

    const binding = await terminal.bindSession({
      providerSessionId: rollout.sessionId,
      mappingVersion: MAPPING_VERSION,
      journal: rollout.journal,
      fingerprint: { kind: "writable-file-below-terminal-process", file: rollout.sourceFile },
    });

    return jsonlSession({
      binding,
      source: new CodexSessionWatcher({ terminal, rollout: rollout.journal, sessionId: rollout.sessionId }),
      mapRecord(record, context) {
        mapCodexRecord(record, context);
      },
    });
  },
});

The full source also enriches the root session title from Codex’s session index and admits child journals only when their native parent ID matches the already-bound root session.

Build the shipped package

Shell
cd extensions/agent-codex
npm install
npm run compile
npm pack
# Upload terminay-agent-codex-0.1.0.tgz in Settings → Extensions

The package requires Node 24 or newer and declares @terminay/extension-api ^1.2.0. See the agent-provider API for the complete contract.