diff --git a/extensions/codex/src/app-server/session-history.ts b/extensions/codex/src/app-server/session-history.ts index 619ab21abf4a..ddeda238a99e 100644 --- a/extensions/codex/src/app-server/session-history.ts +++ b/extensions/codex/src/app-server/session-history.ts @@ -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 { diff --git a/extensions/codex/src/app-server/shared-client.ts b/extensions/codex/src/app-server/shared-client.ts index a5c8bc8557ed..c5b7cd0877b4 100644 --- a/extensions/codex/src/app-server/shared-client.ts +++ b/extensions/codex/src/app-server/shared-client.ts @@ -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 { 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 { @@ -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 { @@ -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 } { diff --git a/extensions/codex/src/app-server/test-support.ts b/extensions/codex/src/app-server/test-support.ts index 3dbd00703590..9075a7e9e238 100644 --- a/extensions/codex/src/app-server/test-support.ts +++ b/extensions/codex/src/app-server/test-support.ts @@ -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[] = []; diff --git a/extensions/codex/src/app-server/user-input-bridge.ts b/extensions/codex/src/app-server/user-input-bridge.ts index 5349905db14b..32c02df997c6 100644 --- a/extensions/codex/src/app-server/user-input-bridge.ts +++ b/extensions/codex/src/app-server/user-input-bridge.ts @@ -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];