Official

codex

Codex agent provider: runs the Codex CLI's app-server for each account.

The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/codex@0.2.0

Permissions in 0.2.0

  • Provide agents agents.provideMediumAdds agents to the app.Provide the Codex agent and pass its tool calls to plugin tools
  • Run named programs processMediumStarts the listed programs.Run the Codex CLI, and ask or tell the installer that owns it (npm or Homebrew) about a newer versionPrograms: codexnpmbrew
  • Environment variables envMediumReads the listed environment variables.Find each account's Codex home, and pass extra app-server arguments set in CODEX_ARGSVariables: HOMECODEX_HOMECODEX_ARGS
  • Read files fs.readMediumReads files in the listed places.Read, once, the accounts the previous Codex provider keptPlaces: its own data folder

Files

testing.test.ts2.6 KB
// Scoped adapter runtime for the provider's original scripted-peer tests.
// A local Promise facade keeps their scenarios and assertions unchanged.
import type { PluginServices } from "convergence/effect";
import { afterEach } from "node:test";
import * as Queue from "effect/Queue";
import * as Effect from "effect/Effect";
import * as Exit from "effect/Exit";
import * as Layer from "effect/Layer";
import * as ManagedRuntime from "effect/ManagedRuntime";
import * as Scope from "effect/Scope";
import { liveLayer } from "convergence/effect";
import type { Api } from "convergence";
import { RpcTransport } from "../sdk/effect.ts";
import { CodexAgent } from "./agent.ts";
import { defaultAccount } from "./instances.ts";

type PromiseMethods<A> = {
  [K in keyof A]: A[K] extends (...args: infer P) => Effect.Effect<infer R, infer _E, infer _S>
    ? (...args: P) => Promise<R>
    : A[K];
};
const cleanups = new Set<() => Promise<void>>();
afterEach(async () => {
  for (const close of cleanups) await close();
  cleanups.clear();
});

export function testAgent(api: Api, env: Record<string, string> = {}) {
  const scope = Scope.makeUnsafe();
  const builtins = liveLayer(
    api,
    (effect) => runtime.runPromise(effect),
    () => {},
  );
  const runtime = ManagedRuntime.make(RpcTransport.layer.pipe(Layer.provideMerge(builtins)));
  let agent: CodexAgent | undefined;
  runtime.runFork(
    Effect.gen(function* () {
      const transport = yield* RpcTransport;
      const jobs = yield* Effect.acquireRelease(
        Queue.make<Effect.Effect<unknown, unknown, PluginServices>>(),
        Queue.shutdown,
      ).pipe(Effect.provideService(Scope.Scope, scope));
      agent = new CodexAgent({ scope, transport, jobs, instance: defaultAccount(), env });
      yield* agent.background();
    }),
  );
  cleanups.add(async () => {
    await runtime.runPromise(Scope.close(scope, Exit.void));
    await runtime.dispose();
  });
  if (!agent) throw new Error("the synchronous adapter did not create the test agent");
  // This locally built proxy changes only Effect methods into Promise methods.
  // Their environment is the same PluginServices provided by this runtime.
  return new Proxy(agent, {
    get(target, key, receiver) {
      const value: unknown = Reflect.get(target, key, receiver);
      if (typeof value !== "function") return value;
      return (...args: unknown[]) => {
        const result: unknown = Reflect.apply(value, target, args);
        return Effect.isEffect(result)
          ? runtime.runPromise(result as Effect.Effect<unknown, unknown, PluginServices>)
          : result;
      };
    },
  }) as unknown as PromiseMethods<CodexAgent>;
}

Versions

VersionPublishedPlugin APISizePermissionsStatus
0.2.0latestOct 5, 2026>=2 <378.1 KB4 permissionsListed

Reviews and comments

0 threads · 0 reviews

No comments yet.