docs: document codex app-server support helpers

This commit is contained in:
Peter Steinberger
2026-06-04 08:50:13 -04:00
parent bd94eda53a
commit e72447de40
4 changed files with 35 additions and 0 deletions

View File

@@ -1,3 +1,7 @@
/**
* Reads OpenClaw session history for Codex transcript mirroring and sanitizes
* image payloads before replaying messages into the app-server projector.
*/
import fs from "node:fs/promises";
import type { AgentMessage } from "openclaw/plugin-sdk/agent-harness-runtime";
import type { SessionEntry } from "openclaw/plugin-sdk/agent-sessions";
@@ -17,6 +21,7 @@ function isMissingFileError(error: unknown): boolean {
);
}
/** Returns sanitized session-context messages for a Codex mirrored session file. */
export async function readCodexMirroredSessionHistoryMessages(
sessionFile: string,
): Promise<AgentMessage[] | undefined> {

View File

@@ -1,3 +1,7 @@
/**
* Owns shared and isolated Codex app-server client startup, auth application,
* lease tracking, and teardown.
*/
import { resolveDefaultAgentDir } from "openclaw/plugin-sdk/agent-runtime";
import {
applyCodexAppServerAuthProfile,
@@ -142,12 +146,14 @@ async function resolveCodexAppServerClientStartContext(
return { agentDir, usesNativeAuth, authProfileId, startOptions };
}
/** Gets or starts a shared Codex app-server client without retaining a lease. */
export async function getSharedCodexAppServerClient(
options?: CodexAppServerClientOptions,
): Promise<CodexAppServerClient> {
return (await acquireSharedCodexAppServerClient(options)).client;
}
/** Gets or starts a shared Codex app-server client and records a release lease. */
export async function getLeasedSharedCodexAppServerClient(
options?: CodexAppServerClientOptions,
): Promise<CodexAppServerClient> {
@@ -159,6 +165,7 @@ export async function getLeasedSharedCodexAppServerClient(
return acquired.client;
}
/** Releases one outstanding lease for a shared Codex app-server client. */
export function releaseLeasedSharedCodexAppServerClient(client: CodexAppServerClient): boolean {
const state = getSharedCodexAppServerClientState();
const releases = state.leasedReleases.get(client);
@@ -260,6 +267,7 @@ async function acquireSharedCodexAppServerClient(
}
}
/** Starts a non-shared Codex app-server client owned entirely by the caller. */
export async function createIsolatedCodexAppServerClient(
options?: CodexAppServerClientOptions,
): Promise<CodexAppServerClient> {
@@ -284,6 +292,7 @@ export async function createIsolatedCodexAppServerClient(
}
}
/** Clears and closes all shared clients for deterministic tests. */
export function resetSharedCodexAppServerClientForTests(): void {
const state = getSharedCodexAppServerClientState();
const clients = collectSharedClients(state);
@@ -294,6 +303,7 @@ export function resetSharedCodexAppServerClientForTests(): void {
}
}
/** Clears and closes all shared clients. */
export function clearSharedCodexAppServerClient(): void {
const state = getSharedCodexAppServerClientState();
const clients = collectSharedClients(state);
@@ -303,6 +313,7 @@ export function clearSharedCodexAppServerClient(): void {
}
}
/** Clears and closes the shared entry only if it still owns the supplied client. */
export function clearSharedCodexAppServerClientIfCurrent(
client: CodexAppServerClient | undefined,
): boolean {
@@ -320,6 +331,7 @@ export function clearSharedCodexAppServerClientIfCurrent(
return false;
}
/** Detaches the shared entry without closing the client when it still matches. */
export function detachSharedCodexAppServerClientIfCurrent(
client: CodexAppServerClient | undefined,
): boolean {
@@ -336,6 +348,7 @@ export function detachSharedCodexAppServerClientIfCurrent(
return false;
}
/** Retains the matching shared client and returns a release callback. */
export function retainSharedCodexAppServerClientIfCurrent(
client: CodexAppServerClient | undefined,
): (() => void) | undefined {
@@ -351,6 +364,7 @@ export function retainSharedCodexAppServerClientIfCurrent(
return undefined;
}
/** Marks a matching shared client to close after active leases/acquires drain. */
export function retireSharedCodexAppServerClientIfCurrent(
client: CodexAppServerClient | undefined,
): { activeLeases: number; closed: boolean } | undefined {
@@ -373,6 +387,7 @@ export function retireSharedCodexAppServerClientIfCurrent(
return undefined;
}
/** Clears a matching shared client and waits for its process to exit. */
export async function clearSharedCodexAppServerClientIfCurrentAndWait(
client: CodexAppServerClient | undefined,
options?: {
@@ -394,6 +409,7 @@ export async function clearSharedCodexAppServerClientIfCurrentAndWait(
return false;
}
/** Clears all shared clients and waits for their processes to exit. */
export async function clearSharedCodexAppServerClientAndWait(options?: {
exitTimeoutMs?: number;
forceKillDelayMs?: number;
@@ -433,6 +449,7 @@ function clearSharedClientEntryIfCurrent(key: string, client: CodexAppServerClie
}
}
/** Clears a matching shared client only when no lease or acquire currently claims it. */
export function clearSharedCodexAppServerClientIfCurrentAndUnclaimed(
client: CodexAppServerClient | undefined,
): { found: boolean; closed: boolean; activeLeases: number; pendingAcquires: number } {

View File

@@ -1,9 +1,14 @@
/**
* Shared Codex app-server test helpers for model fixtures and in-memory client
* transports.
*/
import { EventEmitter } from "node:events";
import { PassThrough, Writable } from "node:stream";
import type { Model } from "openclaw/plugin-sdk/llm";
import { vi } from "vitest";
import { CodexAppServerClient } from "./client.js";
/** Builds a representative Codex-capable model fixture for app-server tests. */
export function createCodexTestModel(provider = "openai", input = ["text"]): Model {
return {
id: "gpt-5.4-codex",
@@ -18,6 +23,7 @@ export function createCodexTestModel(provider = "openai", input = ["text"]): Mod
} as Model;
}
/** Creates an in-memory Codex app-server client harness with writable stdout frames. */
export function createClientHarness() {
const stdout = new PassThrough();
const writes: string[] = [];

View File

@@ -1,3 +1,7 @@
/**
* Bridges Codex item/tool user-input requests to OpenClaw messaging prompts and
* turns replies into app-server answer payloads.
*/
import {
embeddedAgentLog,
type EmbeddedRunAttemptParams,
@@ -44,6 +48,7 @@ type CodexUserInputBridge = {
cancelPending: () => void;
};
/** Creates a per-turn bridge for pending Codex user-input requests. */
export function createCodexUserInputBridge(params: {
paramsForRun: EmbeddedRunAttemptParams;
threadId: string;
@@ -241,6 +246,8 @@ function formatUserInputPrompt(questions: UserInputQuestion[]): string {
}
function buildUserInputResponse(questions: UserInputQuestion[], inputText: string): JsonObject {
// Multi-question replies may use "header: answer" or numbered lines. Keep the
// parser permissive so chat-channel replies remain ergonomic.
const answers: JsonObject = {};
if (questions.length === 1) {
const question = questions[0];