mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-30 19:59:35 +00:00
docs: document cloudflare and codex supervisor plugins
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Public Cloudflare AI Gateway provider helpers shared by onboarding, catalog,
|
||||
* and tests.
|
||||
*/
|
||||
export {
|
||||
buildCloudflareAiGatewayModelDefinition,
|
||||
CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_ID,
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Builds runtime model catalog entries from stored Cloudflare AI Gateway auth
|
||||
* profiles.
|
||||
*/
|
||||
import {
|
||||
coerceSecretRef,
|
||||
resolveNonEnvSecretRefApiKeyMarker,
|
||||
@@ -46,6 +50,10 @@ function resolveCloudflareAiGatewayMetadata(cred: CloudflareAiGatewayCredential)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a provider catalog entry when credentials and Gateway metadata are
|
||||
* complete enough to construct an Anthropic-compatible base URL.
|
||||
*/
|
||||
export function buildCloudflareAiGatewayCatalogProvider(params: {
|
||||
credential: CloudflareAiGatewayCredential;
|
||||
envApiKey?: string;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Bundled provider plugin entry for Cloudflare AI Gateway setup, catalog
|
||||
* discovery, failover classification, and stream wrapping.
|
||||
*/
|
||||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import {
|
||||
applyAuthProfileConfig,
|
||||
@@ -96,6 +100,8 @@ export default definePluginEntry({
|
||||
let capturedSecretInput: Parameters<typeof buildApiKeyCredential>[1] = "";
|
||||
let capturedCredential = false;
|
||||
let capturedMode: "plaintext" | "ref" | undefined;
|
||||
// Capture through the shared provider auth helper so plaintext,
|
||||
// env refs, and secret refs keep the same validation path.
|
||||
await ensureApiKeyFromOptionEnvOrPrompt({
|
||||
token: normalizeOptionalSecretInput(ctx.opts?.cloudflareAiGatewayApiKey),
|
||||
tokenProvider: "cloudflare-ai-gateway",
|
||||
@@ -178,6 +184,8 @@ export default definePluginEntry({
|
||||
return null;
|
||||
}
|
||||
if (resolved.source !== "profile") {
|
||||
// Persist newly supplied credentials with Gateway metadata; a
|
||||
// profile-sourced key already owns its existing auth-store record.
|
||||
const credential = ctx.toApiKeyCredential({
|
||||
provider: PROVIDER_ID,
|
||||
resolved,
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
/**
|
||||
* Model ids, default model metadata, and URL construction for the Cloudflare AI
|
||||
* Gateway provider.
|
||||
*/
|
||||
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
|
||||
/** Provider id used in model refs and auth profiles. */
|
||||
export const CLOUDFLARE_AI_GATEWAY_PROVIDER_ID = "cloudflare-ai-gateway";
|
||||
/** Default Cloudflare AI Gateway model id exposed by the bundled provider. */
|
||||
export const CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_ID = "claude-sonnet-4-6";
|
||||
/** Fully-qualified default model ref used by onboarding. */
|
||||
export const CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF = `${CLOUDFLARE_AI_GATEWAY_PROVIDER_ID}/${CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_ID}`;
|
||||
|
||||
const CLOUDFLARE_AI_GATEWAY_DEFAULT_CONTEXT_WINDOW = 200_000;
|
||||
@@ -13,6 +20,10 @@ const CLOUDFLARE_AI_GATEWAY_DEFAULT_COST = {
|
||||
cacheWrite: 3.75,
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds a provider model definition, allowing tests/catalog code to override
|
||||
* the model id while preserving Cloudflare defaults.
|
||||
*/
|
||||
export function buildCloudflareAiGatewayModelDefinition(params?: {
|
||||
id?: string;
|
||||
name?: string;
|
||||
@@ -31,6 +42,10 @@ export function buildCloudflareAiGatewayModelDefinition(params?: {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the Anthropic Messages base URL for a Cloudflare account/gateway
|
||||
* pair, returning an empty string for incomplete metadata.
|
||||
*/
|
||||
export function resolveCloudflareAiGatewayBaseUrl(params: {
|
||||
accountId: string;
|
||||
gatewayId: string;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Config patch helpers used by Cloudflare AI Gateway interactive and
|
||||
* non-interactive onboarding flows.
|
||||
*/
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithDefaultModel,
|
||||
@@ -9,6 +13,9 @@ import {
|
||||
resolveCloudflareAiGatewayBaseUrl,
|
||||
} from "./models.js";
|
||||
|
||||
/**
|
||||
* Builds the minimal config patch for provider setup and default model aliasing.
|
||||
*/
|
||||
export function buildCloudflareAiGatewayConfigPatch(params: {
|
||||
accountId: string;
|
||||
gatewayId: string;
|
||||
@@ -36,6 +43,9 @@ export function buildCloudflareAiGatewayConfigPatch(params: {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies provider model config while preserving existing agent model aliases.
|
||||
*/
|
||||
export function applyCloudflareAiGatewayProviderConfig(
|
||||
cfg: OpenClawConfig,
|
||||
params?: { accountId?: string; gatewayId?: string },
|
||||
@@ -80,6 +90,9 @@ export function applyCloudflareAiGatewayProviderConfig(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies Cloudflare AI Gateway config and makes its default model primary.
|
||||
*/
|
||||
export function applyCloudflareAiGatewayConfig(
|
||||
cfg: OpenClawConfig,
|
||||
params?: { accountId?: string; gatewayId?: string },
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Stream wrapper for Cloudflare AI Gateway's Anthropic Messages compatibility
|
||||
* quirks.
|
||||
*/
|
||||
import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
|
||||
import type { ProviderWrapStreamFnContext } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createAnthropicThinkingPrefillPayloadWrapper } from "openclaw/plugin-sdk/provider-stream-shared";
|
||||
@@ -9,6 +13,10 @@ function shouldPatchAnthropicMessagesPayload(model: ProviderWrapStreamFnContext[
|
||||
return model?.api === undefined || model.api === "anthropic-messages";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapper that removes trailing assistant prefill messages before
|
||||
* extended-thinking Anthropic requests are sent through Cloudflare.
|
||||
*/
|
||||
export function createCloudflareAiGatewayAnthropicThinkingPrefillWrapper(
|
||||
baseStreamFn: StreamFn | undefined,
|
||||
): StreamFn {
|
||||
@@ -19,6 +27,9 @@ export function createCloudflareAiGatewayAnthropicThinkingPrefillWrapper(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the Anthropic payload wrapper only for Anthropic-compatible models.
|
||||
*/
|
||||
export function wrapCloudflareAiGatewayProviderStream(
|
||||
ctx: ProviderWrapStreamFnContext,
|
||||
): StreamFn | undefined {
|
||||
@@ -28,5 +39,6 @@ export function wrapCloudflareAiGatewayProviderStream(
|
||||
return createCloudflareAiGatewayAnthropicThinkingPrefillWrapper(ctx.streamFn);
|
||||
}
|
||||
|
||||
/** Test-only access to wrapper decisions and logger injection points. */
|
||||
export const testing = { log, shouldPatchAnthropicMessagesPayload };
|
||||
export { testing as __testing };
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Bundled plugin entry that exposes Codex app-server supervisor tools to
|
||||
* OpenClaw agents.
|
||||
*/
|
||||
import { buildJsonPluginConfigSchema, definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import {
|
||||
CodexSupervisorPluginConfigSchema,
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Public Codex Supervisor API barrel for plugin tools, MCP serving, config, and
|
||||
* session types.
|
||||
*/
|
||||
export {
|
||||
CodexSupervisorPluginConfigSchema,
|
||||
loadCodexSupervisorEndpoints,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* Config parsing for Codex Supervisor endpoints and safety gates.
|
||||
*/
|
||||
import { Type, type Static } from "typebox";
|
||||
import type { CodexSupervisorEndpoint } from "./types.js";
|
||||
|
||||
@@ -26,6 +29,9 @@ const WebSocketEndpointSchema = Type.Object(
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
|
||||
/**
|
||||
* Plugin config schema accepted by the bundled plugin manifest.
|
||||
*/
|
||||
export const CodexSupervisorPluginConfigSchema = Type.Object(
|
||||
{
|
||||
endpoints: Type.Optional(
|
||||
@@ -37,8 +43,10 @@ export const CodexSupervisorPluginConfigSchema = Type.Object(
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
|
||||
/** Raw plugin config shape accepted from OpenClaw config. */
|
||||
export type CodexSupervisorPluginConfig = Static<typeof CodexSupervisorPluginConfigSchema>;
|
||||
|
||||
/** Normalized config consumed by plugin registration and MCP serving. */
|
||||
export type ResolvedCodexSupervisorPluginConfig = {
|
||||
endpoints: CodexSupervisorEndpoint[];
|
||||
allowRawTranscripts: boolean;
|
||||
@@ -140,6 +148,10 @@ function endpointFromToken(token: string, index: number): CodexSupervisorEndpoin
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads endpoint definitions from environment, defaulting to the local Codex
|
||||
* app-server unix socket.
|
||||
*/
|
||||
export function loadCodexSupervisorEndpoints(
|
||||
env: Pick<NodeJS.ProcessEnv, string> = process.env,
|
||||
): CodexSupervisorEndpoint[] {
|
||||
@@ -185,6 +197,9 @@ function normalizeConfiguredEndpoints(
|
||||
return normalized.length > 0 ? requireUniqueEndpointIds(normalized) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves raw plugin config and env endpoints into validated runtime config.
|
||||
*/
|
||||
export function resolveCodexSupervisorPluginConfig(
|
||||
rawConfig: unknown,
|
||||
env: Pick<NodeJS.ProcessEnv, string> = process.env,
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* JSON-RPC transports for Codex app-server connections over stdio proxies or
|
||||
* websocket/unix-socket endpoints.
|
||||
*/
|
||||
import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import * as net from "node:net";
|
||||
@@ -28,6 +32,10 @@ function formatMalformedMessageError(error: unknown): Error {
|
||||
return new Error(`Malformed Codex app-server message: ${detail}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces denial responses for app-server approval requests the supervisor
|
||||
* deliberately cannot grant.
|
||||
*/
|
||||
export function resolveSafeApprovalResult(method: string): Record<string, unknown> | undefined {
|
||||
if (method === "item/tool/call") {
|
||||
return {
|
||||
@@ -121,6 +129,8 @@ abstract class BaseCodexJsonRpcConnection implements CodexJsonRpcConnection {
|
||||
const method = typeof message.method === "string" ? message.method : undefined;
|
||||
if (id !== undefined && method) {
|
||||
const result = resolveSafeApprovalResult(method);
|
||||
// The supervisor is read/steer tooling, not a native approval delegate;
|
||||
// unknown app-server requests fail closed with either a denial or -32601.
|
||||
this.sendRaw(
|
||||
JSON.stringify(
|
||||
result === undefined
|
||||
@@ -339,6 +349,10 @@ class WebSocketCodexJsonRpcConnection extends BaseCodexJsonRpcConnection {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens, initializes, and returns a JSON-RPC connection for one supervisor
|
||||
* endpoint.
|
||||
*/
|
||||
export async function connectCodexAppServerEndpoint(
|
||||
endpoint: CodexSupervisorEndpoint,
|
||||
): Promise<CodexJsonRpcConnection> {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Standalone MCP stdio server for exposing Codex Supervisor tools to trusted
|
||||
* MCP clients.
|
||||
*/
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { loadCodexSupervisorEndpoints } from "./config.js";
|
||||
@@ -18,11 +22,15 @@ function routeLogsToStderr(): void {
|
||||
}
|
||||
}
|
||||
|
||||
/** Options for creating or serving a Codex Supervisor MCP server. */
|
||||
export type CodexSupervisorMcpServeOptions = {
|
||||
supervisor?: CodexSupervisor;
|
||||
toolOptions?: CodexSupervisorMcpToolOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an MCP server and owns the supervisor instance unless one is supplied.
|
||||
*/
|
||||
export function createCodexSupervisorMcpServer(opts: CodexSupervisorMcpServeOptions = {}): {
|
||||
server: McpServer;
|
||||
supervisor: CodexSupervisor;
|
||||
@@ -41,6 +49,10 @@ export function createCodexSupervisorMcpServer(opts: CodexSupervisorMcpServeOpti
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Serves Codex Supervisor tools over MCP stdio until transport or process
|
||||
* shutdown.
|
||||
*/
|
||||
export async function serveCodexSupervisorMcp(
|
||||
opts: CodexSupervisorMcpServeOptions = {},
|
||||
): Promise<void> {
|
||||
@@ -63,6 +75,8 @@ export async function serveCodexSupervisorMcp(
|
||||
process.stdin.off("close", shutdown);
|
||||
process.off("SIGINT", shutdown);
|
||||
process.off("SIGTERM", shutdown);
|
||||
// The SDK exposes this callback slot but not a stable setter; clear it so
|
||||
// close() cannot recursively re-enter shutdown.
|
||||
transport["onclose"] = undefined;
|
||||
close().then(resolveClosed, resolveClosed);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* MCP tool registration plus redaction helpers for Codex Supervisor sessions
|
||||
* and endpoint metadata.
|
||||
*/
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { z } from "zod";
|
||||
import type { CodexSupervisor } from "./supervisor.js";
|
||||
@@ -7,9 +11,12 @@ import type {
|
||||
CodexSupervisorSessionListResult,
|
||||
} from "./types.js";
|
||||
|
||||
/** Env gate for exposing transcript-derived fields through standalone MCP. */
|
||||
export const RAW_TRANSCRIPTS_ENV = "OPENCLAW_CODEX_SUPERVISOR_ALLOW_RAW_TRANSCRIPTS";
|
||||
/** Env gate for mutating/steering Codex sessions through standalone MCP. */
|
||||
export const WRITE_CONTROLS_ENV = "OPENCLAW_CODEX_SUPERVISOR_ALLOW_WRITE_CONTROLS";
|
||||
|
||||
/** Optional policy callbacks for standalone MCP tool exposure. */
|
||||
export type CodexSupervisorMcpToolOptions = {
|
||||
rawTranscriptReadsAllowed?: () => boolean;
|
||||
writeControlsAllowed?: () => boolean;
|
||||
@@ -36,6 +43,10 @@ function redactString(value: string): string {
|
||||
.replace(/\bBearer\s+[-._~+/a-zA-Z0-9]+=*/g, "Bearer [redacted]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Redacts common secret-bearing fields and token-like substrings before tool
|
||||
* results leave the supervisor.
|
||||
*/
|
||||
export function redactCodexSupervisorValue(value: unknown, key = ""): unknown {
|
||||
if (typeof value === "string") {
|
||||
if (/authorization|password|secret|token|api[-_]?key/i.test(key)) {
|
||||
@@ -74,6 +85,7 @@ function redactEndpointUrl(value: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns endpoint metadata safe for tool results. */
|
||||
export function redactCodexSupervisorEndpoint(
|
||||
endpoint: CodexSupervisorEndpoint,
|
||||
): Record<string, unknown> {
|
||||
@@ -115,6 +127,10 @@ function sanitizeSessionForMcp(
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes session-list output, optionally including transcript-derived
|
||||
* preview/name fields only when the caller has opted in.
|
||||
*/
|
||||
export function sanitizeCodexSupervisorSessionListResult(
|
||||
result: CodexSupervisorSessionListResult,
|
||||
includeTranscriptDerivedFields = rawTranscriptReadsAllowed(),
|
||||
@@ -129,6 +145,10 @@ export function sanitizeCodexSupervisorSessionListResult(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers MCP tools for endpoint probing, session listing, reads, sends, and
|
||||
* interrupts.
|
||||
*/
|
||||
export function registerCodexSupervisorMcpTools(
|
||||
server: McpServer,
|
||||
supervisor: CodexSupervisor,
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* OpenClaw agent-tool definitions for Codex Supervisor endpoint and session
|
||||
* controls.
|
||||
*/
|
||||
import { jsonResult, readStringParam, type AnyAgentTool } from "openclaw/plugin-sdk/core";
|
||||
import { Type } from "typebox";
|
||||
import {
|
||||
@@ -48,11 +52,13 @@ const SessionInterruptParamsSchema = Type.Object(
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
|
||||
/** Policy flags controlling transcript reads and write operations. */
|
||||
export type CodexSupervisorToolPolicy = {
|
||||
allowRawTranscripts: boolean;
|
||||
allowWriteControls: boolean;
|
||||
};
|
||||
|
||||
/** Dependencies needed to build OpenClaw agent tools. */
|
||||
export type CodexSupervisorToolOptions = {
|
||||
supervisor: CodexSupervisor;
|
||||
policy: CodexSupervisorToolPolicy;
|
||||
@@ -105,6 +111,10 @@ function requireWriteAccess(policy: CodexSupervisorToolPolicy): void {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the OpenClaw tools that expose Codex endpoint health and session
|
||||
* controls.
|
||||
*/
|
||||
export function createCodexSupervisorTools({
|
||||
supervisor,
|
||||
policy,
|
||||
@@ -151,6 +161,8 @@ export function createCodexSupervisorTools({
|
||||
description: "Read one Codex session transcript from app-server.",
|
||||
parameters: SessionReadParamsSchema,
|
||||
execute: async (_toolCallId, rawParams) => {
|
||||
// Raw transcript access is opt-in because app-server sessions can hold
|
||||
// secrets, private files, and user-authenticated browser context.
|
||||
requireRawTranscriptAccess(policy);
|
||||
const params = asRecord(rawParams);
|
||||
const threadId = readStringParam(params, "thread_id", { required: true });
|
||||
@@ -172,6 +184,8 @@ export function createCodexSupervisorTools({
|
||||
"Send text to a Codex session. Idle sessions start a turn; active sessions are steered.",
|
||||
parameters: SessionSendParamsSchema,
|
||||
execute: async (_toolCallId, rawParams) => {
|
||||
// Session write controls can steer or interrupt a human-visible Codex
|
||||
// turn, so they remain behind an explicit plugin policy gate.
|
||||
requireWriteAccess(policy);
|
||||
const params = asRecord(rawParams);
|
||||
const result = await supervisor.sendToSession({
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Codex app-server supervisor that lists sessions, reads transcripts, and
|
||||
* starts/steers/interrupts turns across configured endpoints.
|
||||
*/
|
||||
import { connectCodexAppServerEndpoint } from "./json-rpc-client.js";
|
||||
import type {
|
||||
CodexJsonRpcConnection,
|
||||
@@ -109,6 +113,7 @@ function isLoadedThreadReadMiss(error: unknown): boolean {
|
||||
return message.includes("thread not found") || message.includes("thread not loaded");
|
||||
}
|
||||
|
||||
/** High-level supervisor facade used by OpenClaw tools and MCP tools. */
|
||||
export class CodexSupervisor {
|
||||
private readonly connections = new Map<string, Promise<CodexJsonRpcConnection>>();
|
||||
|
||||
@@ -117,10 +122,12 @@ export class CodexSupervisor {
|
||||
private readonly connector: EndpointConnector = connectCodexAppServerEndpoint,
|
||||
) {}
|
||||
|
||||
/** Returns configured endpoint definitions without opening connections. */
|
||||
listEndpoints(): CodexSupervisorEndpoint[] {
|
||||
return this.endpoints;
|
||||
}
|
||||
|
||||
/** Closes all open app-server connections owned by this supervisor. */
|
||||
async close(): Promise<void> {
|
||||
const settled = await Promise.allSettled(this.connections.values());
|
||||
this.connections.clear();
|
||||
@@ -133,6 +140,7 @@ export class CodexSupervisor {
|
||||
);
|
||||
}
|
||||
|
||||
/** Checks whether each endpoint can service a lightweight thread list call. */
|
||||
async probeEndpoints(): Promise<CodexSupervisorEndpointHealth[]> {
|
||||
return await Promise.all(
|
||||
this.endpoints.map(async (endpoint) => {
|
||||
@@ -152,12 +160,14 @@ export class CodexSupervisor {
|
||||
);
|
||||
}
|
||||
|
||||
/** Lists sessions, returning only the session array for agent-tool callers. */
|
||||
async listSessions(
|
||||
params: { includeStored?: boolean; maxStoredSessions?: number } = {},
|
||||
): Promise<CodexSupervisorSession[]> {
|
||||
return (await this.listSessionSnapshot(params)).sessions;
|
||||
}
|
||||
|
||||
/** Lists sessions plus endpoint errors for structured tool output. */
|
||||
async listSessionSnapshot(
|
||||
params: { includeStored?: boolean; maxStoredSessions?: number } = {},
|
||||
): Promise<CodexSupervisorSessionListResult> {
|
||||
@@ -178,6 +188,7 @@ export class CodexSupervisor {
|
||||
return { sessions, errors };
|
||||
}
|
||||
|
||||
/** Reads a single Codex session transcript from the resolved endpoint. */
|
||||
async readSession(params: {
|
||||
endpointId?: string;
|
||||
threadId: string;
|
||||
@@ -201,6 +212,7 @@ export class CodexSupervisor {
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts a new turn or steers an active turn depending on requested mode. */
|
||||
async sendToSession(params: {
|
||||
endpointId?: string;
|
||||
threadId: string;
|
||||
@@ -224,6 +236,8 @@ export class CodexSupervisor {
|
||||
if (mode === "steer" || status === "active") {
|
||||
const detailed = await this.readThread(connection, params.threadId, true);
|
||||
const detailedThread = extractThread(detailed);
|
||||
// Active-turn ids may appear in full thread turns or the summary API;
|
||||
// try both before failing so steering handles materialized and lazy turns.
|
||||
const turnId =
|
||||
(detailedThread ? findInProgressTurnId(detailedThread) : undefined) ??
|
||||
findInProgressTurnId(thread) ??
|
||||
@@ -247,6 +261,7 @@ export class CodexSupervisor {
|
||||
}
|
||||
}
|
||||
|
||||
/** Interrupts an active Codex turn, resolving the turn id when omitted. */
|
||||
async interruptSession(params: {
|
||||
endpointId?: string;
|
||||
threadId: string;
|
||||
@@ -285,6 +300,8 @@ export class CodexSupervisor {
|
||||
endpoint,
|
||||
params.maxStoredSessions,
|
||||
)) {
|
||||
// Loaded sessions are authoritative for attachment/status; append stored
|
||||
// history only for threads that are not already live.
|
||||
if (!sessions.some((session) => session.threadId === stored.threadId)) {
|
||||
sessions.push(stored);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Public Codex Supervisor endpoint, session, and JSON-RPC connection types.
|
||||
*/
|
||||
/** Configured transport target for a Codex app-server endpoint. */
|
||||
export type CodexSupervisorEndpoint =
|
||||
| {
|
||||
id: string;
|
||||
@@ -15,10 +19,13 @@ export type CodexSupervisorEndpoint =
|
||||
authTokenEnv?: string;
|
||||
};
|
||||
|
||||
/** Send behavior requested by supervisor write tools. */
|
||||
export type CodexSupervisorTurnMode = "auto" | "start" | "steer";
|
||||
|
||||
/** App-server thread status string, preserved for forward compatibility. */
|
||||
export type CodexSupervisorThreadStatus = string;
|
||||
|
||||
/** Normalized session summary returned by supervisor list operations. */
|
||||
export type CodexSupervisorSession = {
|
||||
endpointId: string;
|
||||
threadId: string;
|
||||
@@ -32,6 +39,7 @@ export type CodexSupervisorSession = {
|
||||
humanAttached?: boolean;
|
||||
};
|
||||
|
||||
/** Result returned after starting or steering a Codex turn. */
|
||||
export type CodexSupervisorSendResult = {
|
||||
endpointId: string;
|
||||
threadId: string;
|
||||
@@ -40,18 +48,21 @@ export type CodexSupervisorSendResult = {
|
||||
status?: string;
|
||||
};
|
||||
|
||||
/** Minimal JSON-RPC connection contract used by the supervisor. */
|
||||
export type CodexJsonRpcConnection = {
|
||||
request(method: string, params?: Record<string, unknown>): Promise<unknown>;
|
||||
notify(method: string, params?: Record<string, unknown>): void;
|
||||
close(): Promise<void>;
|
||||
};
|
||||
|
||||
/** Health result for one configured supervisor endpoint. */
|
||||
export type CodexSupervisorEndpointHealth = {
|
||||
endpointId: string;
|
||||
ok: boolean;
|
||||
detail?: string;
|
||||
};
|
||||
|
||||
/** Session list plus endpoint errors for tool-friendly structured output. */
|
||||
export type CodexSupervisorSessionListResult = {
|
||||
sessions: CodexSupervisorSession[];
|
||||
errors: CodexSupervisorEndpointHealth[];
|
||||
|
||||
Reference in New Issue
Block a user