@jrkropp/codex-js 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @jrkropp/codex-js
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add high-level app-server helpers, dynamic tool definition helpers, connection
8
+ snapshot support, and pending server-request persistence contracts.
9
+ - Align public server terminology around app-server, connection, thread, turn,
10
+ dynamic tool, server request, server notification, and transport concepts.
11
+ - Replace the placeholder Cloudflare example with a deployable Worker + Durable
12
+ Object + Vite React example.
13
+
3
14
  ## 0.2.0
4
15
 
5
16
  ### Minor Changes
package/README.md CHANGED
@@ -1,99 +1,148 @@
1
1
  # @jrkropp/codex-js
2
2
 
3
- Core TypeScript runtime, browser client, server adapters, and test utilities for building Codex-backed apps.
3
+ Core TypeScript SDK for building Codex-backed applications. It includes the browser app-server client, platform-neutral server helpers, Codex-aligned runtime contracts, stores, model transport, and test utilities.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```sh
8
- pnpm add @jrkropp/codex-js
8
+ npm install @jrkropp/codex-js
9
9
  ```
10
10
 
11
11
  Requirements:
12
12
 
13
13
  - Node.js 20 or newer.
14
- - ESM projects only. This package does not ship CommonJS.
15
- - React is not a dependency of this package. Install `@jrkropp/codex-js-react` only when you need the packaged UI.
14
+ - ESM only. CommonJS output is not shipped.
15
+ - React is not a dependency. Install `@jrkropp/codex-js-react` only when you need packaged UI components.
16
16
 
17
- ## Stable Imports
17
+ ## Public Imports
18
18
 
19
19
  ```ts
20
20
  import { createCodexAppServerClient } from "@jrkropp/codex-js/client";
21
- import { createCodexAppServerRuntime } from "@jrkropp/codex-js/server";
21
+ import { createCodexAppServer } from "@jrkropp/codex-js/server";
22
22
  import { InMemoryThreadStore } from "@jrkropp/codex-js/testing";
23
23
  ```
24
24
 
25
- The published core package exposes only:
25
+ The core package exposes only:
26
26
 
27
27
  - `@jrkropp/codex-js`
28
28
  - `@jrkropp/codex-js/client`
29
29
  - `@jrkropp/codex-js/server`
30
30
  - `@jrkropp/codex-js/testing`
31
31
 
32
- There are no public upstream mirror or unstable import paths.
32
+ There are no public mirror or unstable imports.
33
33
 
34
- ## Browser Client
34
+ ## Server Quick Start
35
35
 
36
36
  ```ts
37
- import { createCodexAppServerClient } from "@jrkropp/codex-js/client";
37
+ import {
38
+ createCodexAppServer,
39
+ createModelClient,
40
+ defineDynamicTool,
41
+ dynamicToolResponse,
42
+ } from "@jrkropp/codex-js/server";
43
+ import { InMemoryThreadStore } from "@jrkropp/codex-js/testing";
38
44
 
39
- const appServer = createCodexAppServerClient({
40
- url: () => "ws://localhost:1466/api/codex/app-server",
45
+ const lookupStatus = defineDynamicTool({
46
+ name: "lookup_status",
47
+ description: "Look up the current deployment status.",
48
+ inputSchema: {
49
+ type: "object",
50
+ properties: { name: { type: "string" } },
51
+ required: ["name"],
52
+ additionalProperties: false,
53
+ },
54
+ async execute(args) {
55
+ return dynamicToolResponse.text(`${args.name} is healthy.`);
56
+ },
41
57
  });
42
58
 
43
- await appServer.requestTyped("thread/start", {
44
- threadId: "00000000-0000-4000-8000-000000000001",
59
+ const appServer = createCodexAppServer({
60
+ threadStore: new InMemoryThreadStore(),
61
+ dynamicTools: [lookupStatus],
62
+ defaults: {
63
+ cwd: "/workspace",
64
+ model: "gpt-5-mini",
65
+ modelProvider: "openai",
66
+ },
67
+ createModelClient({ session, threadId }) {
68
+ return createModelClient({
69
+ apiKey: process.env.OPENAI_API_KEY!,
70
+ installationId: "my-app",
71
+ sessionId: session.id,
72
+ threadId,
73
+ });
74
+ },
45
75
  });
46
76
  ```
47
77
 
48
- ## Server Runtime
78
+ Create one app-server connection per WebSocket:
49
79
 
50
80
  ```ts
51
- import {
52
- CodexAppServerMessageProcessor,
53
- createCodexAppServerRuntime,
54
- } from "@jrkropp/codex-js/server";
55
- import { InMemoryThreadStore } from "@jrkropp/codex-js/testing";
81
+ const connection = appServer.createConnection({
82
+ send(message) {
83
+ webSocket.send(message);
84
+ },
85
+ });
56
86
 
57
- const runtime = createCodexAppServerRuntime({
58
- threadStore: new InMemoryThreadStore(),
87
+ webSocket.addEventListener("message", (event) => {
88
+ void connection.accept(event.data);
59
89
  });
60
90
 
61
- const processor = new CodexAppServerMessageProcessor({
62
- runtime,
63
- send: (message) => {
64
- // Write the serialized app-server event to your WebSocket.
65
- console.log(message);
66
- },
91
+ webSocket.addEventListener("close", () => {
92
+ void connection.close();
67
93
  });
68
94
  ```
69
95
 
70
- Create one message processor per WebSocket connection. The package does not own your HTTP server, credential handling, persistence backend, or product-specific tools.
96
+ `createCodexAppServerRuntime` is still exported for advanced hosts that need to own message processing directly, but most applications should start with `createCodexAppServer`.
71
97
 
72
- ## React UI
98
+ ## Dynamic Tools
73
99
 
74
- The UI package is separate:
100
+ Use `defineDynamicTool` for server-executed tools. Use a namespace when a tool is deferred and loaded through Codex tool search.
75
101
 
76
- ```sh
77
- pnpm add @jrkropp/codex-js @jrkropp/codex-js-react react react-dom
102
+ ```ts
103
+ const lookupInvoice = defineDynamicTool({
104
+ namespace: "billing",
105
+ name: "lookup_invoice",
106
+ description: "Look up an invoice by id.",
107
+ deferLoading: true,
108
+ inputSchema: {
109
+ type: "object",
110
+ properties: { invoiceId: { type: "string" } },
111
+ required: ["invoiceId"],
112
+ additionalProperties: false,
113
+ },
114
+ async execute(args) {
115
+ return dynamicToolResponse.text(`Invoice ${args.invoiceId} is paid.`);
116
+ },
117
+ });
78
118
  ```
79
119
 
80
- ```tsx
81
- import { CodexChat } from "@jrkropp/codex-js-react";
82
- import "@jrkropp/codex-js-react/styles.css";
120
+ Tools with `execute` are resolved by the server. Tools without `execute` are surfaced as app-server requests so the client can resolve them.
121
+
122
+ ## Browser Client
123
+
124
+ ```ts
125
+ import { createCodexAppServerClient } from "@jrkropp/codex-js/client";
126
+
127
+ const appServer = createCodexAppServerClient({
128
+ url: async () => {
129
+ const session = await fetch("/api/codex/session", { method: "POST" });
130
+ const { webSocketUrl } = await session.json();
131
+ return webSocketUrl;
132
+ },
133
+ });
83
134
  ```
84
135
 
85
- ## Examples
136
+ The browser never needs an OpenAI API key. Host applications should issue a short-lived app-server WebSocket URL from their backend.
137
+
138
+ ## Cloudflare
86
139
 
87
- From the repository root:
140
+ The repository includes a deployable Cloudflare Worker + Durable Object + Vite React example:
88
141
 
89
142
  ```sh
90
- pnpm dev:minimal
91
- pnpm dev:vite-react
143
+ pnpm dev:node-local
92
144
  pnpm dev:cloudflare-example
145
+ pnpm --filter @jrkropp/codex-js-cloudflare-example deploy:dry-run
93
146
  ```
94
147
 
95
- The examples use local source aliases during development and packed-package tests verify the npm tarballs.
96
-
97
- ## Architecture
98
-
99
- `@jrkropp/codex-js` is the non-React package. It owns the transport protocol, app-server client, server runtime helpers, stores, serializers, and testing primitives. UI components, shadcn exports, Tailwind output, and React-only dependencies live in `@jrkropp/codex-js-react`.
148
+ Use `examples/node-local` for the smallest local Node/Vite integration. Use `examples/cloudflare` for a deployable Worker + Durable Object integration with one-time WebSocket tickets, Durable Object SQLite storage, hibernating WebSockets, server-executed dynamic tools, and a deferred namespaced tool.
@@ -1,5 +1,5 @@
1
1
  import type { EventMsg } from "../../core/src/protocol.js";
2
2
  import { serverRequestResolvedNotification, type AppServerProtocolEvent, type EventMappingContext, type ServerRequestCoreTarget } from "../../app-server-protocol/src/protocol/event-mapping.js";
3
3
  export type { AppServerProtocolEvent, EventMappingContext, ServerRequestCoreTarget, };
4
- export declare function apply_bespoke_event_handling(msg: EventMsg, context: EventMappingContext): AppServerProtocolEvent[];
4
+ export declare function mapCoreEventToAppServerEvents(msg: EventMsg, context: EventMappingContext): AppServerProtocolEvent[];
5
5
  export { serverRequestResolvedNotification };
@@ -36,6 +36,15 @@ export type InitializedConnectionSessionState = {
36
36
  experimentalApiEnabled: boolean;
37
37
  optedOutNotificationMethods: ReadonlySet<string>;
38
38
  };
39
+ export type CodexAppServerConnectionSnapshot = {
40
+ initialized: InitializedConnectionSessionSnapshot | null;
41
+ };
42
+ export type InitializedConnectionSessionSnapshot = {
43
+ appServerClientName: string;
44
+ clientVersion: string;
45
+ experimentalApiEnabled: boolean;
46
+ optedOutNotificationMethods: string[];
47
+ };
39
48
  export declare class CodexAppServerConnectionSessionState {
40
49
  readonly rpcGate: ConnectionRpcGate;
41
50
  private initializedState;
@@ -46,6 +55,8 @@ export declare class CodexAppServerConnectionSessionState {
46
55
  optedOutNotificationMethods(): ReadonlySet<string>;
47
56
  appServerClientName(): string | null;
48
57
  clientVersion(): string | null;
58
+ snapshot(): CodexAppServerConnectionSnapshot;
59
+ static fromSnapshot(snapshot?: CodexAppServerConnectionSnapshot | null): CodexAppServerConnectionSessionState;
49
60
  }
50
61
  export type CodexAppServerMessageProcessorOptions<Context = unknown> = {
51
62
  connectionId?: ConnectionId;
@@ -3,7 +3,7 @@ import type { ResponseStream, ResponsesWsRequest, TurnState } from "../common.js
3
3
  type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
4
4
  type WorkerWebSocket = WebSocket & {
5
5
  accept?: (options?: unknown) => void;
6
- binaryType?: BinaryType;
6
+ binaryType?: "arraybuffer" | "blob";
7
7
  };
8
8
  export type ResponsesWebsocketClientInput = {
9
9
  api_key: string;
@@ -15,12 +15,12 @@ export { CodexAppServerClientTransportError, createCodexAppServerClient, parseCo
15
15
  export { jsonRpcInternalError, jsonRpcInvalidRequestError, jsonRpcParseError, parseClientTransportPayload, parseJsonRpcMessage, parseJsonRpcTransportPayload, parseServerTransportPayload, queuedOutgoingMessage, serializeJsonRpcError, serializeJsonRpcResponse, serializeOutgoingMessage, type ConnectionOrigin, type JSONRPCError, type JSONRPCMessage, type JSONRPCNotification, type JSONRPCRequest, type JSONRPCResponse, type ParsedClientTransportMessage, type ParsedServerTransportMessage, type ParsedTransportPayload, type TransportEvent, } from "../internal/codex/app-server-transport/src/transport/mod.js";
16
16
  export { OutgoingMessageSender, ThreadScopedOutgoingMessageSender, outgoingMessageToAppServerEvent, type ConnectionId, type ConnectionRequestId, type OutgoingError, type OutgoingMessage, type OutgoingResponse, type QueuedOutgoingMessage, type RequestContext, } from "../internal/codex/app-server/src/outgoing_message.js";
17
17
  export { CodexAppServerRequestError } from "../internal/codex/app-server/src/request_processors/request_errors.js";
18
- export { codexAppServerDeferredResponse, CodexAppServerConnectionSessionState, CodexAppServerMessageProcessor, type CodexAppServerMethodHandlers, type CodexAppServerRequestContext, type CodexAppServerDeferredResponse, type CodexAppServerConnectionRequestOutcome, type CodexAppServerMessageProcessorOptions, type InitializedConnectionSessionState, } from "../internal/codex/app-server/src/message_processor.js";
18
+ export { codexAppServerDeferredResponse, CodexAppServerConnectionSessionState, CodexAppServerMessageProcessor, type CodexAppServerMethodHandlers, type CodexAppServerRequestContext, type CodexAppServerDeferredResponse, type CodexAppServerConnectionSnapshot, type CodexAppServerConnectionRequestOutcome, type CodexAppServerMessageProcessorOptions, type InitializedConnectionSessionSnapshot, type InitializedConnectionSessionState, } from "../internal/codex/app-server/src/message_processor.js";
19
19
  export { ConnectionRpcGate, ConnectionRpcGateClosedError, } from "../internal/codex/app-server/src/connection_rpc_gate.js";
20
20
  export { RequestSerializationQueues, requestSerializationQueueKeyFromScope, type RequestSerializationQueueKey, } from "../internal/codex/app-server/src/request_serialization.js";
21
21
  export { McpRequestProcessor, ThreadRequestProcessor, TurnRequestProcessor, type RuntimeSession, } from "../internal/codex/app-server/src/request_processors.js";
22
22
  export { createCodexAppServerRuntime, type CodexAppServerEventSink, type CodexAppServerOutgoingSink, type CodexAppServerRuntime, type CodexAppServerRuntimeContext, type CodexAppServerRuntimeOptions, } from "../internal/codex/app-server/src/runtime.js";
23
- export { AppServerSession, type CodexAppServer } from "../internal/codex/app-server-client/src/session.js";
23
+ export { AppServerSession, type CodexAppServer, } from "../internal/codex/app-server-client/src/session.js";
24
24
  export { serverNotificationThreadTarget, serverRequestThreadId, threadEventSnapshotHasStarted, ThreadEventStore, type ServerNotificationThreadTarget, type ThreadBufferedEvent, type ThreadEventSnapshot, type ThreadTokenUsageSnapshot, } from "../internal/codex/app-server-client/src/thread_event_store.js";
25
25
  export { PendingAppServerRequests, type AppServerRequestResolution, type ResolvedAppServerRequest, type UnsupportedAppServerRequest, } from "../internal/codex/app-server-client/src/pending_requests.js";
26
26
  export { deniedRequestPermissionsResponse } from "../internal/codex/core/src/request_permissions.js";
@@ -36,7 +36,7 @@ export { ThreadHistoryBuilder } from "../internal/codex/core/src/thread-history-
36
36
  export type { Turn as ThreadHistoryTurn, TurnStatus as ThreadHistoryTurnStatus, } from "../internal/codex/core/src/thread-history-builder.js";
37
37
  export type { TextElement as CoreTextElement, UserInput as CoreUserInputItem, } from "../internal/codex/core/src/protocol/user_input.js";
38
38
  export type { TurnItem as CoreTurnItem, UserMessageTurnItem as CoreUserMessageTurnItem, } from "../internal/codex/core/src/items.js";
39
- export type { Model, ModelPreset } from "../internal/codex/core/src/model-provider.js";
39
+ export type { Model, ModelPreset, } from "../internal/codex/core/src/model-provider.js";
40
40
  export { createModelClient, defaultModelsManager, ModelClientSession, ResponsesClient, ResponsesWebsocketClient, ResponsesWebsocketConnection, resolveReasoningEffortForModel, } from "../internal/codex/core/src/index.js";
41
41
  export type { CreateModelClientInput, ModelClient, ModelClientSessionHandle, Prompt, ResponseEvent, ResponseStream, ResponsesApiRequest, ResponsesClientInput, ResponseCreateWsRequest, ResponseProcessedWsRequest, ResponsesWsRequest, } from "../internal/codex/core/src/index.js";
42
42
  export { ThreadMemoryMode } from "../internal/codex/core/src/memory.js";
@@ -0,0 +1,60 @@
1
+ import { type CodexAppServerRuntime, type CodexAppServerRuntimeContext, type CodexAppServerRuntimeOptions, type CodexAppServerConnectionSnapshot, type ConnectionId, type JSONRPCErrorError, type RequestId, type ServerRequest, type ThreadStore } from "../runtime/index.js";
2
+ import { type DefinedDynamicTool } from "./dynamic-tools.js";
3
+ export type PendingServerRequestRecord = {
4
+ createdAt: number;
5
+ request: ServerRequest;
6
+ requestId: RequestId;
7
+ threadId: string;
8
+ };
9
+ export type PendingServerRequestStore = {
10
+ delete(requestId: RequestId): Promise<void> | void;
11
+ get(requestId: RequestId): Promise<PendingServerRequestRecord | null> | PendingServerRequestRecord | null;
12
+ list(): Promise<PendingServerRequestRecord[]> | PendingServerRequestRecord[];
13
+ put(record: PendingServerRequestRecord): Promise<void> | void;
14
+ take(requestId: RequestId): Promise<PendingServerRequestRecord | null> | PendingServerRequestRecord | null;
15
+ };
16
+ export type CodexAppServerDefaults = {
17
+ baseInstructions?: string;
18
+ cwd?: string;
19
+ model?: string;
20
+ modelProvider?: string;
21
+ source?: string;
22
+ threadSource?: string | null;
23
+ };
24
+ export type CreateCodexAppServerOptions<Context = CodexAppServerRuntimeContext> = Omit<CodexAppServerRuntimeOptions<Context>, "sendOutgoingTransportMessage" | "store"> & {
25
+ defaults?: CodexAppServerDefaults;
26
+ dynamicTools?: readonly DefinedDynamicTool<Context>[];
27
+ pendingServerRequests?: PendingServerRequestStore;
28
+ /** @deprecated Use threadStore. */
29
+ store?: ThreadStore;
30
+ threadStore?: ThreadStore;
31
+ };
32
+ export type CodexAppServerConnection<Context = CodexAppServerRuntimeContext> = {
33
+ accept(message: unknown): Promise<void>;
34
+ close(): Promise<void>;
35
+ connectionId: ConnectionId;
36
+ processor: ReturnType<CodexAppServerRuntime<Context>["createMessageProcessor"]>;
37
+ snapshot(): CodexAppServerConnectionSnapshot;
38
+ };
39
+ export type CreateCodexAppServerConnectionOptions<Context = CodexAppServerRuntimeContext> = {
40
+ connectionId?: ConnectionId;
41
+ context?: Context;
42
+ onSnapshot?: (snapshot: CodexAppServerConnectionSnapshot) => void | Promise<void>;
43
+ send(message: string): void | Promise<void>;
44
+ snapshot?: CodexAppServerConnectionSnapshot | null;
45
+ };
46
+ export type CreatedCodexAppServer<Context = CodexAppServerRuntimeContext> = CodexAppServerRuntime<Context> & {
47
+ createConnection(options: CreateCodexAppServerConnectionOptions<Context>): CodexAppServerConnection<Context>;
48
+ pendingServerRequests: PendingServerRequestStore;
49
+ };
50
+ export declare class InMemoryPendingServerRequestStore implements PendingServerRequestStore {
51
+ private readonly records;
52
+ delete(requestId: RequestId): void;
53
+ get(requestId: RequestId): PendingServerRequestRecord | null;
54
+ list(): PendingServerRequestRecord[];
55
+ put(record: PendingServerRequestRecord): void;
56
+ take(requestId: RequestId): PendingServerRequestRecord | null;
57
+ }
58
+ export declare function createCodexAppServer<Context = CodexAppServerRuntimeContext>(options: CreateCodexAppServerOptions<Context>): CreatedCodexAppServer<Context>;
59
+ export declare function createCodexAppServerConnection<Context = CodexAppServerRuntimeContext>(appServer: CreatedCodexAppServer<Context>, options: CreateCodexAppServerConnectionOptions<Context>): CodexAppServerConnection<Context>;
60
+ export declare function jsonRpcErrorFromUnknown(error: unknown): JSONRPCErrorError;
@@ -0,0 +1,40 @@
1
+ import type { DynamicToolCallParams, DynamicToolCallResponse, DynamicToolSpec } from "../runtime/index.js";
2
+ export type DynamicToolExecutionContext<Context = unknown> = {
3
+ callId: string;
4
+ context?: Context;
5
+ namespace: string | null;
6
+ params: DynamicToolCallParams;
7
+ threadId: string;
8
+ tool: string;
9
+ turnId: string;
10
+ };
11
+ export type DynamicToolExecute<Context = unknown, Args = unknown> = (args: Args, context: DynamicToolExecutionContext<Context>) => DynamicToolCallResponse | Promise<DynamicToolCallResponse>;
12
+ export type DynamicToolDefinition<Context = unknown, Args = unknown> = {
13
+ deferLoading?: boolean;
14
+ description: string;
15
+ execute?: DynamicToolExecute<Context, Args>;
16
+ inputSchema: unknown;
17
+ name: string;
18
+ namespace?: string | null;
19
+ };
20
+ export type DefinedDynamicTool<Context = unknown, Args = unknown> = Readonly<DynamicToolDefinition<Context, Args>>;
21
+ export type DefinedDynamicToolset<Context = unknown> = readonly DefinedDynamicTool<Context>[];
22
+ type DynamicToolDefinitionForValidation = {
23
+ deferLoading?: boolean;
24
+ description: string;
25
+ inputSchema: unknown;
26
+ name: string;
27
+ namespace?: string | null;
28
+ };
29
+ export declare const dynamicToolResponse: {
30
+ text(text: string): DynamicToolCallResponse;
31
+ image(imageUrl: string): DynamicToolCallResponse;
32
+ error(message: string): DynamicToolCallResponse;
33
+ };
34
+ export declare function defineDynamicTool<Context = unknown, Args = unknown>(definition: DynamicToolDefinition<Context, Args>): DefinedDynamicTool<Context, Args>;
35
+ export declare function defineDynamicToolset<Context = unknown>(definitions: readonly DynamicToolDefinition<Context>[]): DefinedDynamicToolset<Context>;
36
+ export declare function dynamicToolSpecFromDefinition(tool: DynamicToolDefinitionForValidation): DynamicToolSpec;
37
+ export declare function dynamicToolSpecsFromDefinitions(tools: readonly DefinedDynamicTool[]): DynamicToolSpec[];
38
+ export declare function findDynamicTool<Context>(tools: readonly DefinedDynamicTool<Context>[], params: DynamicToolCallParams): DefinedDynamicTool<Context> | null;
39
+ export declare function validateDynamicToolDefinitions(tools: readonly DynamicToolDefinitionForValidation[]): void;
40
+ export {};
@@ -1,2 +1,7 @@
1
1
  export { AppServerSession, asThreadId, BaseInstructions, CodexAppServerClientTransportError, CodexAppServerConnectionSessionState, codexAppServerDeferredResponse, CodexAppServerMessageProcessor, CodexAppServerRequestError, CodexMcpConnectionManagerAdapter, ConnectionRpcGate, ConnectionRpcGateClosedError, createCodexAppServerClient, createCodexAppServerRuntime, createModelClient, deniedRequestPermissionsResponse, EmptyMcpConnectionManager, InMemoryThreadStore, jsonRpcInternalError, jsonRpcInvalidRequestError, jsonRpcParseError, LiveThread, LocalThreadStore, McpRequestProcessor, ModelClientSession, OutgoingMessageSender, outgoingMessageToAppServerEvent, parseClientTransportPayload, parseCodexAppServerEvent, parseJsonRpcMessage, parseJsonRpcTransportPayload, parseServerTransportPayload, PendingAppServerRequests, queuedOutgoingMessage, requestMethodName, RequestSerializationQueues, requestSerializationQueueKeyFromScope, requestTyped, ResponsesClient, ResponsesWebsocketClient, ResponsesWebsocketConnection, serializeJsonRpcError, serializeJsonRpcResponse, serializeOutgoingMessage, serverNotificationRequiresDelivery, serverNotificationThreadTarget, serverRequestThreadId, setRenderedThreadConnectionStatus, SortDirection, StaticMcpConnectionManager, ThreadEventPersistenceMode, threadEventSnapshotHasStarted, ThreadEventStore, ThreadHistoryBuilder, ThreadMemoryMode, ThreadRequestProcessor, ThreadScopedOutgoingMessageSender, ThreadSortKey, TurnRequestProcessor, } from "../runtime/index.js";
2
- export type { AppendThreadItemsParams, AppServerEvent, AppServerRequestHandle, ClientRequest, ClientRequestSerializationScope, CodexAppServer, CodexAppServerClientConnectionStatus, CodexAppServerClientOptions, CodexAppServerDeferredResponse, CodexAppServerEventSink, CodexAppServerMethodHandlers, CodexAppServerOutgoingSink, CodexAppServerRequestContext, CodexAppServerRuntime, CodexAppServerRuntimeContext, CodexAppServerRuntimeOptions, CodexAppServerRuntimeOptions as CreateCodexAppServerRuntimeOptions, CodexAppServerRuntime as CreatedCodexAppServerRuntime, CollaborationMode, CollaborationModeListParams, CollaborationModeListResponse, CommandExecutionRequestApprovalResponse, ConnectionId, ConnectionOrigin, ConnectionRequestId, CreateModelClientInput, CreateThreadParams, DynamicToolCallParams, DynamicToolCallResponse, DynamicToolSpec, DynamicToolSpecWire, FileChangeRequestApprovalResponse, InitializedConnectionSessionState, InMemoryThreadStoreCalls, JSONRPCError, JSONRPCErrorError, JSONRPCMessage, JSONRPCNotification, JSONRPCRequest, JSONRPCResponse, ListMcpServerStatusParams, ListMcpServerStatusResponse, LocalThreadStoreConfig, McpConnectionManager, McpResourceInfo, McpResourceReadParams, McpResourceReadResponse, McpResourceTemplateInfo, McpRuntimeEnvironment, McpServerElicitationRequestResponse, McpServerOauthLoginParams, McpServerOauthLoginResponse, McpServerRefreshConfig, McpServerRefreshResponse, McpServerStatus, McpServerStatusListOptions, McpServerToolCallParams, McpServerToolCallResponse, McpToolInfo, Model, ModelClient, ModelClientSessionHandle, ModelPreset, OutgoingError, OutgoingMessage, OutgoingResponse, ParsedClientTransportMessage, ParsedServerTransportMessage, ParsedTransportPayload, PermissionsRequestApprovalResponse, Prompt, QueuedOutgoingMessage, RequestContext, RequestId, RequestSerializationQueueKey, ResponseCreateWsRequest, ResponseEvent, ResponseProcessedWsRequest, ResponseStream, ResponsesApiRequest, ResponsesClientInput, ResponsesWsRequest, Result, RuntimeSession, SandboxPolicy, ServerNotification, ServerNotificationThreadTarget, ServerRequest, StoredThread, StoredThreadHistory, Thread, ThreadArchiveParams, ThreadArchiveResponse, ThreadBufferedEvent, ThreadCompactStartParams, ThreadCompactStartResponse, ThreadEventSnapshot, ThreadId, ThreadItem, ThreadListParams, ThreadListResponse, ThreadMetadataPatch, ThreadMetadataUpdateParams, ThreadMetadataUpdateResponse, ThreadPage, ThreadPersistenceMetadata, ThreadReadParams, ThreadReadResponse, ThreadResumeParams, ThreadResumeResponse, ThreadSetNameParams, ThreadSetNameResponse, ThreadStartParams, ThreadStartResponse, ThreadStore, ThreadTokenUsageSnapshot, ThreadUnarchiveParams, ThreadUnarchiveResponse, ToolRequestUserInputResponse, TransportEvent, Turn, TurnInterruptParams, TurnInterruptResponse, TurnStartParams, TurnStartResponse, TurnSteerParams, TurnSteerResponse, TypedRequestError, UserInput, } from "../runtime/index.js";
2
+ export { createCodexAppServer, createCodexAppServerConnection, InMemoryPendingServerRequestStore, jsonRpcErrorFromUnknown, } from "./app-server.js";
3
+ export { dynamicToolResponse, defineDynamicTool, defineDynamicToolset, dynamicToolSpecFromDefinition, dynamicToolSpecsFromDefinitions, findDynamicTool, validateDynamicToolDefinitions, } from "./dynamic-tools.js";
4
+ export { dynamicToolResponse as codexDynamicToolResponse, defineDynamicTool as defineCodexDynamicTool, defineDynamicToolset as defineCodexDynamicToolset, } from "./dynamic-tools.js";
5
+ export type { AppendThreadItemsParams, AppServerEvent, AppServerRequestHandle, ArchiveThreadParams, ClientRequest, ClientRequestSerializationScope, CodexAppServer, CodexAppServerClientConnectionStatus, CodexAppServerClientOptions, CodexAppServerDeferredResponse, CodexAppServerEventSink, CodexAppServerMethodHandlers, CodexAppServerOutgoingSink, CodexAppServerRequestContext, CodexAppServerRuntime, CodexAppServerRuntimeContext, CodexAppServerRuntimeOptions, CodexAppServerConnectionSnapshot, CodexAppServerRuntimeOptions as CreateCodexAppServerRuntimeOptions, CodexAppServerRuntime as CreatedCodexAppServerRuntime, CollaborationMode, CollaborationModeListParams, CollaborationModeListResponse, CommandExecutionRequestApprovalResponse, ConnectionId, ConnectionOrigin, ConnectionRequestId, CreateModelClientInput, CreateThreadParams, DynamicToolCallParams, DynamicToolCallResponse, DynamicToolSpec, DynamicToolSpecWire, FileChangeRequestApprovalResponse, InitializedConnectionSessionState, InMemoryThreadStoreCalls, JSONRPCError, JSONRPCErrorError, JSONRPCMessage, JSONRPCNotification, JSONRPCRequest, JSONRPCResponse, ListMcpServerStatusParams, ListMcpServerStatusResponse, ListThreadsParams, LocalThreadStoreConfig, LoadThreadHistoryParams, McpConnectionManager, McpResourceInfo, McpResourceReadParams, McpResourceReadResponse, McpResourceTemplateInfo, McpRuntimeEnvironment, McpServerElicitationRequestResponse, McpServerOauthLoginParams, McpServerOauthLoginResponse, McpServerRefreshConfig, McpServerRefreshResponse, McpServerStatus, McpServerStatusListOptions, McpServerToolCallParams, McpServerToolCallResponse, McpToolInfo, Model, ModelClient, ModelClientSessionHandle, ModelPreset, OutgoingError, OutgoingMessage, OutgoingResponse, ParsedClientTransportMessage, ParsedServerTransportMessage, ParsedTransportPayload, PermissionsRequestApprovalResponse, Prompt, QueuedOutgoingMessage, RequestContext, RequestId, RequestSerializationQueueKey, ReadThreadByRolloutPathParams, ReadThreadParams, ResumeThreadParams, ResponseCreateWsRequest, ResponseEvent, ResponseProcessedWsRequest, ResponseStream, ResponsesApiRequest, ResponsesClientInput, ResponsesWsRequest, Result, RolloutItem, RuntimeSession, SandboxPolicy, ServerNotification, ServerNotificationThreadTarget, ServerRequest, StoredThread, StoredThreadHistory, Thread, ThreadArchiveParams, ThreadArchiveResponse, ThreadBufferedEvent, ThreadCompactStartParams, ThreadCompactStartResponse, ThreadEventSnapshot, ThreadId, ThreadItem, ThreadListParams, ThreadListResponse, ThreadMetadataPatch, ThreadMetadataUpdateParams, ThreadMetadataUpdateResponse, ThreadPage, ThreadPersistenceMetadata, UpdateThreadMetadataParams, ThreadReadParams, ThreadReadResponse, ThreadResumeParams, ThreadResumeResponse, ThreadSetNameParams, ThreadSetNameResponse, ThreadStartParams, ThreadStartResponse, ThreadStore, ThreadTokenUsageSnapshot, ThreadUnarchiveParams, ThreadUnarchiveResponse, ToolRequestUserInputResponse, TransportEvent, Turn, TurnInterruptParams, TurnInterruptResponse, TurnStartParams, TurnStartResponse, TurnSteerParams, TurnSteerResponse, TypedRequestError, UserInput, } from "../runtime/index.js";
6
+ export type { CodexAppServerConnection, CodexAppServerDefaults, CreateCodexAppServerConnectionOptions, CreateCodexAppServerOptions, CreatedCodexAppServer, PendingServerRequestRecord, PendingServerRequestStore, } from "./app-server.js";
7
+ export type { DefinedDynamicTool, DefinedDynamicToolset, DynamicToolDefinition, DynamicToolExecute, DynamicToolExecutionContext, } from "./dynamic-tools.js";
@@ -4006,7 +4006,7 @@ function requestSerializationQueueKeyFromScope(connectionId, scope) {
4006
4006
  }
4007
4007
 
4008
4008
  // src/internal/codex/app-server/src/message_processor.ts
4009
- var CodexAppServerConnectionSessionState = class {
4009
+ var CodexAppServerConnectionSessionState = class _CodexAppServerConnectionSessionState {
4010
4010
  rpcGate = new ConnectionRpcGate();
4011
4011
  initializedState = null;
4012
4012
  constructor(initializedState) {
@@ -4033,6 +4033,31 @@ var CodexAppServerConnectionSessionState = class {
4033
4033
  clientVersion() {
4034
4034
  return this.initializedState?.clientVersion ?? null;
4035
4035
  }
4036
+ snapshot() {
4037
+ return {
4038
+ initialized: this.initializedState ? {
4039
+ appServerClientName: this.initializedState.appServerClientName,
4040
+ clientVersion: this.initializedState.clientVersion,
4041
+ experimentalApiEnabled: this.initializedState.experimentalApiEnabled,
4042
+ optedOutNotificationMethods: Array.from(
4043
+ this.initializedState.optedOutNotificationMethods
4044
+ )
4045
+ } : null
4046
+ };
4047
+ }
4048
+ static fromSnapshot(snapshot) {
4049
+ if (!snapshot?.initialized) {
4050
+ return new _CodexAppServerConnectionSessionState();
4051
+ }
4052
+ return new _CodexAppServerConnectionSessionState({
4053
+ appServerClientName: snapshot.initialized.appServerClientName,
4054
+ clientVersion: snapshot.initialized.clientVersion,
4055
+ experimentalApiEnabled: snapshot.initialized.experimentalApiEnabled,
4056
+ optedOutNotificationMethods: new Set(
4057
+ snapshot.initialized.optedOutNotificationMethods
4058
+ )
4059
+ });
4060
+ }
4036
4061
  };
4037
4062
  var CODEX_APP_SERVER_DEFERRED_RESPONSE = {
4038
4063
  __codexAppServerDeferredResponse: true
@@ -6477,8 +6502,7 @@ var WsStream = class {
6477
6502
  waiters = [];
6478
6503
  closed = false;
6479
6504
  is_closed() {
6480
- const closedState = typeof WebSocket !== "undefined" ? WebSocket.CLOSED : this.socket.CLOSED;
6481
- return this.closed || this.socket.readyState === (closedState ?? 3);
6505
+ return this.closed || this.socket.readyState === 3;
6482
6506
  }
6483
6507
  send(message) {
6484
6508
  if (this.is_closed()) {
@@ -6504,9 +6528,11 @@ var WsStream = class {
6504
6528
  let timeout = null;
6505
6529
  try {
6506
6530
  return await Promise.race([
6507
- new Promise((resolve, reject) => {
6508
- this.waiters.push({ resolve, reject });
6509
- }),
6531
+ new Promise(
6532
+ (resolve, reject) => {
6533
+ this.waiters.push({ resolve, reject });
6534
+ }
6535
+ ),
6510
6536
  new Promise((_, reject) => {
6511
6537
  if (timeoutMs && timeoutMs > 0) {
6512
6538
  timeout = setTimeout(
@@ -6760,7 +6786,11 @@ function closeLikeEvent() {
6760
6786
  reason: "websocket closed"
6761
6787
  });
6762
6788
  }
6763
- return { type: "close", code: 1006, reason: "websocket closed" };
6789
+ return {
6790
+ type: "close",
6791
+ code: 1006,
6792
+ reason: "websocket closed"
6793
+ };
6764
6794
  }
6765
6795
  function isCloseEvent(event) {
6766
6796
  return event.type === "close";
@@ -19851,8 +19881,8 @@ var ThreadStateManager = class {
19851
19881
  }
19852
19882
  };
19853
19883
 
19854
- // src/internal/codex/app-server/src/bespoke_event_handling.ts
19855
- function apply_bespoke_event_handling(msg, context) {
19884
+ // src/internal/codex/app-server/src/app_server_event_mapping.ts
19885
+ function mapCoreEventToAppServerEvents(msg, context) {
19856
19886
  return eventMsgToAppServerEvents(msg, context);
19857
19887
  }
19858
19888
 
@@ -19884,7 +19914,11 @@ function createCodexAppServerRuntime(options) {
19884
19914
  });
19885
19915
  async function emit(threadId, event, context) {
19886
19916
  if (event.type === "server_notification") {
19887
- await outgoing.sendServerNotification(event.notification, context, threadId);
19917
+ await outgoing.sendServerNotification(
19918
+ event.notification,
19919
+ context,
19920
+ threadId
19921
+ );
19888
19922
  return;
19889
19923
  }
19890
19924
  if (event.type === "server_request") {
@@ -19913,7 +19947,7 @@ function createCodexAppServerRuntime(options) {
19913
19947
  }
19914
19948
  const threadState = threadStateManager.threadState(threadId);
19915
19949
  const tracked = threadState.trackCurrentTurnEvent(event.id, event.msg);
19916
- const protocolEvents = apply_bespoke_event_handling(event.msg, {
19950
+ const protocolEvents = mapCoreEventToAppServerEvents(event.msg, {
19917
19951
  activeTurn: tracked.activeTurn,
19918
19952
  terminalTurn: tracked.terminalTurn,
19919
19953
  threadId,
@@ -19992,9 +20026,7 @@ function createCodexAppServerRuntime(options) {
19992
20026
  async function resolveServerRequest(params) {
19993
20027
  const threadId = asThreadId(String(params.threadId));
19994
20028
  const targetKey = serverRequestTargetKey(threadId, params.requestId);
19995
- const request2 = outgoing.pendingRequestsForThread(threadId).find(
19996
- (candidate) => candidate.id === params.requestId
19997
- ) ?? null;
20029
+ const request2 = outgoing.pendingRequestsForThread(threadId).find((candidate) => candidate.id === params.requestId) ?? null;
19998
20030
  if (!request2) {
19999
20031
  serverRequestTargets.delete(targetKey);
20000
20032
  }
@@ -20008,10 +20040,13 @@ function createCodexAppServerRuntime(options) {
20008
20040
  await submitServerRequestResponse(threadId, submission);
20009
20041
  serverRequestTargets.delete(targetKey);
20010
20042
  await outgoing.notifyClientResponse(params.requestId, params.result);
20011
- await emit(threadId, serverRequestResolvedNotification({
20012
- requestId: params.requestId,
20013
- threadId
20014
- }));
20043
+ await emit(
20044
+ threadId,
20045
+ serverRequestResolvedNotification({
20046
+ requestId: params.requestId,
20047
+ threadId
20048
+ })
20049
+ );
20015
20050
  }
20016
20051
  async function connectionClosed(connectionId) {
20017
20052
  const emptyThreadIds = threadStateManager.removeConnection(connectionId);
@@ -20026,9 +20061,7 @@ function createCodexAppServerRuntime(options) {
20026
20061
  async function rejectServerRequest(params) {
20027
20062
  const threadId = asThreadId(String(params.threadId));
20028
20063
  const targetKey = serverRequestTargetKey(threadId, params.requestId);
20029
- const request2 = outgoing.pendingRequestsForThread(threadId).find(
20030
- (candidate) => candidate.id === params.requestId
20031
- ) ?? null;
20064
+ const request2 = outgoing.pendingRequestsForThread(threadId).find((candidate) => candidate.id === params.requestId) ?? null;
20032
20065
  if (!request2) {
20033
20066
  serverRequestTargets.delete(targetKey);
20034
20067
  }
@@ -20042,18 +20075,25 @@ function createCodexAppServerRuntime(options) {
20042
20075
  await submitServerRequestResponse(threadId, submission);
20043
20076
  serverRequestTargets.delete(targetKey);
20044
20077
  await outgoing.notifyClientError(params.requestId, params.error);
20045
- await emit(threadId, serverRequestResolvedNotification({
20046
- requestId: params.requestId,
20047
- threadId
20048
- }));
20078
+ await emit(
20079
+ threadId,
20080
+ serverRequestResolvedNotification({
20081
+ requestId: params.requestId,
20082
+ threadId
20083
+ })
20084
+ );
20049
20085
  }
20050
20086
  async function submitServerRequestResponse(threadId, submission) {
20051
20087
  if (!isServerRequestResponseSubmission(submission)) {
20052
- throw new Error("Only server-request response submissions can be routed through CodexAppServerRuntime.");
20088
+ throw new Error(
20089
+ "Only server-request response submissions can be routed through CodexAppServerRuntime."
20090
+ );
20053
20091
  }
20054
20092
  const runtimeSession = sessions.get(threadId);
20055
20093
  if (!runtimeSession) {
20056
- throw new Error("Codex thread has no active turn for this server request response.");
20094
+ throw new Error(
20095
+ "Codex thread has no active turn for this server request response."
20096
+ );
20057
20097
  }
20058
20098
  await runtimeSession.session.submit_with_id(submission);
20059
20099
  }
@@ -20485,4 +20525,437 @@ function dirname(path) {
20485
20525
  return separatorIndex <= 0 ? "/" : normalized.slice(0, separatorIndex);
20486
20526
  }
20487
20527
 
20488
- export { AppServerSession, BaseInstructions, CodexAppServerClientTransportError, CodexAppServerConnectionSessionState, CodexAppServerMessageProcessor, CodexAppServerRequestError, CodexMcpConnectionManagerAdapter, ConnectionRpcGate, ConnectionRpcGateClosedError, EmptyMcpConnectionManager, InMemoryThreadStore, LiveThread, LocalThreadStore, McpRequestProcessor, ModelClientSession, OutgoingMessageSender, PendingAppServerRequests, RequestSerializationQueues, ResponsesClient, ResponsesWebsocketClient, ResponsesWebsocketConnection, SortDirection, StaticMcpConnectionManager, ThreadEventPersistenceMode, ThreadEventStore, ThreadHistoryBuilder, ThreadMemoryMode, ThreadRequestProcessor, ThreadScopedOutgoingMessageSender, ThreadSortKey, TurnRequestProcessor, asThreadId, codexAppServerDeferredResponse, createCodexAppServerClient, createCodexAppServerRuntime, createModelClient, deniedRequestPermissionsResponse, jsonRpcInternalError, jsonRpcInvalidRequestError, jsonRpcParseError, outgoingMessageToAppServerEvent, parseClientTransportPayload, parseCodexAppServerEvent, parseJsonRpcMessage, parseJsonRpcTransportPayload, parseServerTransportPayload, queuedOutgoingMessage, requestMethodName, requestSerializationQueueKeyFromScope, requestTyped, serializeJsonRpcError, serializeJsonRpcResponse, serializeOutgoingMessage, serverNotificationRequiresDelivery, serverNotificationThreadTarget, serverRequestThreadId, setRenderedThreadConnectionStatus, threadEventSnapshotHasStarted };
20528
+ // src/server/dynamic-tools.ts
20529
+ var RESPONSES_API_TOOL_NAME = /^[A-Za-z0-9_-]+$/u;
20530
+ var RESPONSES_API_TOOL_NAME_MAX_LENGTH = 64;
20531
+ var dynamicToolResponse = {
20532
+ text(text) {
20533
+ return {
20534
+ contentItems: [{ text, type: "inputText" }],
20535
+ success: true
20536
+ };
20537
+ },
20538
+ image(imageUrl) {
20539
+ return {
20540
+ contentItems: [{ imageUrl, type: "inputImage" }],
20541
+ success: true
20542
+ };
20543
+ },
20544
+ error(message) {
20545
+ return {
20546
+ contentItems: [{ text: message, type: "inputText" }],
20547
+ success: false
20548
+ };
20549
+ }
20550
+ };
20551
+ function defineDynamicTool(definition) {
20552
+ const tool = Object.freeze({
20553
+ ...definition,
20554
+ deferLoading: definition.deferLoading ?? false,
20555
+ namespace: definition.namespace ?? null
20556
+ });
20557
+ validateDynamicToolDefinitions([tool]);
20558
+ return tool;
20559
+ }
20560
+ function defineDynamicToolset(definitions) {
20561
+ const tools = definitions.map(
20562
+ (definition) => Object.freeze({
20563
+ ...definition,
20564
+ deferLoading: definition.deferLoading ?? false,
20565
+ namespace: definition.namespace ?? null
20566
+ })
20567
+ );
20568
+ validateDynamicToolDefinitions(tools);
20569
+ return Object.freeze(tools);
20570
+ }
20571
+ function dynamicToolSpecFromDefinition(tool) {
20572
+ return {
20573
+ defer_loading: tool.deferLoading ?? false,
20574
+ description: tool.description,
20575
+ input_schema: tool.inputSchema,
20576
+ name: tool.name,
20577
+ namespace: tool.namespace ?? null
20578
+ };
20579
+ }
20580
+ function dynamicToolSpecsFromDefinitions(tools) {
20581
+ return tools.map(dynamicToolSpecFromDefinition);
20582
+ }
20583
+ function findDynamicTool(tools, params) {
20584
+ const namespace = params.namespace ?? null;
20585
+ return tools.find(
20586
+ (tool) => (tool.namespace ?? null) === namespace && tool.name === params.tool
20587
+ ) ?? null;
20588
+ }
20589
+ function validateDynamicToolDefinitions(tools) {
20590
+ const names = /* @__PURE__ */ new Set();
20591
+ for (const tool of tools) {
20592
+ validateResponsesApiName(tool.name, "name");
20593
+ if (tool.namespace) {
20594
+ validateResponsesApiName(tool.namespace, "namespace");
20595
+ }
20596
+ if ((tool.deferLoading ?? false) && !tool.namespace) {
20597
+ throw new Error(
20598
+ `Dynamic tool ${tool.name} uses deferLoading and must include a namespace.`
20599
+ );
20600
+ }
20601
+ validateInputSchema(tool.name, tool.inputSchema);
20602
+ const key = `${tool.namespace ?? ""}:${tool.name}`;
20603
+ if (names.has(key)) {
20604
+ throw new Error(
20605
+ `Duplicate dynamic tool registration for ${tool.namespace ? `${tool.namespace}/` : ""}${tool.name}.`
20606
+ );
20607
+ }
20608
+ names.add(key);
20609
+ }
20610
+ }
20611
+ function validateResponsesApiName(value, label) {
20612
+ if (value.length === 0 || value.length > RESPONSES_API_TOOL_NAME_MAX_LENGTH || !RESPONSES_API_TOOL_NAME.test(value)) {
20613
+ throw new Error(
20614
+ `Dynamic tool ${label} ${value} is not supported by the Responses API. Tool names and namespaces may only contain letters, numbers, underscores, and hyphens.`
20615
+ );
20616
+ }
20617
+ }
20618
+ function validateInputSchema(toolName, schema) {
20619
+ if (!isRecord15(schema)) {
20620
+ throw new Error(
20621
+ `Dynamic tool ${toolName} inputSchema must be a JSON Schema object.`
20622
+ );
20623
+ }
20624
+ if (schema.type !== "object") {
20625
+ throw new Error(
20626
+ `Dynamic tool ${toolName} inputSchema must use type "object".`
20627
+ );
20628
+ }
20629
+ if ("properties" in schema && !isRecord15(schema.properties)) {
20630
+ throw new Error(
20631
+ `Dynamic tool ${toolName} inputSchema.properties must be an object when present.`
20632
+ );
20633
+ }
20634
+ if ("required" in schema && !isStringArray(schema.required)) {
20635
+ throw new Error(
20636
+ `Dynamic tool ${toolName} inputSchema.required must be a string array when present.`
20637
+ );
20638
+ }
20639
+ }
20640
+ function isRecord15(value) {
20641
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20642
+ }
20643
+ function isStringArray(value) {
20644
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
20645
+ }
20646
+
20647
+ // src/server/app-server.ts
20648
+ var nextConnectionId = 1;
20649
+ var InMemoryPendingServerRequestStore = class {
20650
+ records = /* @__PURE__ */ new Map();
20651
+ delete(requestId) {
20652
+ this.records.delete(requestId);
20653
+ }
20654
+ get(requestId) {
20655
+ return this.records.get(requestId) ?? null;
20656
+ }
20657
+ list() {
20658
+ return Array.from(this.records.values());
20659
+ }
20660
+ put(record) {
20661
+ this.records.set(record.requestId, record);
20662
+ }
20663
+ take(requestId) {
20664
+ const record = this.get(requestId);
20665
+ this.delete(requestId);
20666
+ return record;
20667
+ }
20668
+ };
20669
+ function createCodexAppServer(options) {
20670
+ const threadStore = options.threadStore ?? options.store;
20671
+ if (!threadStore) {
20672
+ throw new Error("createCodexAppServer requires threadStore.");
20673
+ }
20674
+ const dynamicTools = [...options.dynamicTools ?? []];
20675
+ const pendingServerRequests = options.pendingServerRequests ?? new InMemoryPendingServerRequestStore();
20676
+ const connections = /* @__PURE__ */ new Map();
20677
+ const subscriptions = /* @__PURE__ */ new Map();
20678
+ const runtime = createCodexAppServerRuntime({
20679
+ ...options,
20680
+ store: threadStore,
20681
+ buildCreateThreadParams: async (input) => {
20682
+ const base = options.buildCreateThreadParams ? await options.buildCreateThreadParams(input) : {
20683
+ base_instructions: {
20684
+ text: input.params.baseInstructions ?? options.defaults?.baseInstructions ?? BaseInstructions.default().text
20685
+ },
20686
+ dynamic_tools: [],
20687
+ event_persistence_mode: ThreadEventPersistenceMode.Limited,
20688
+ metadata: {
20689
+ cwd: input.params.cwd ?? options.defaults?.cwd ?? "/",
20690
+ memory_mode: ThreadMemoryMode.Disabled,
20691
+ model: input.params.model ?? options.defaults?.model ?? "gpt-5-mini",
20692
+ model_provider: input.params.modelProvider ?? options.defaults?.modelProvider ?? "openai"
20693
+ },
20694
+ source: options.defaults?.source ?? "appServer",
20695
+ thread_id: input.threadId,
20696
+ thread_source: typeof input.params.threadSource === "string" ? input.params.threadSource : options.defaults?.threadSource ?? null
20697
+ };
20698
+ const resolvedTools = (await options.resolveDynamicTools?.(input))?.map((tool) => tool) ?? [];
20699
+ return {
20700
+ ...base,
20701
+ dynamic_tools: [
20702
+ ...base.dynamic_tools ?? [],
20703
+ ...resolvedTools,
20704
+ ...dynamicTools.map(dynamicToolSpecFromDefinition)
20705
+ ]
20706
+ };
20707
+ },
20708
+ buildSessionConfiguration: async (input) => {
20709
+ const config = await options.buildSessionConfiguration?.(input) ?? {};
20710
+ return {
20711
+ ...config,
20712
+ dynamic_tools: [
20713
+ ...config.dynamic_tools ?? [],
20714
+ ...dynamicTools.map(dynamicToolSpecFromDefinition)
20715
+ ]
20716
+ };
20717
+ },
20718
+ sendOutgoingTransportMessage: async (message, messageContext) => {
20719
+ const handled = await maybeExecuteDynamicTool({
20720
+ context: messageContext.context,
20721
+ dynamicTools,
20722
+ message,
20723
+ runtime,
20724
+ threadId: messageContext.threadId
20725
+ });
20726
+ if (handled) {
20727
+ return;
20728
+ }
20729
+ if (isServerRequest2(message)) {
20730
+ const threadId = messageContext.threadId ?? serverRequestThreadId(message);
20731
+ if (threadId) {
20732
+ await pendingServerRequests.put({
20733
+ createdAt: Date.now(),
20734
+ request: message,
20735
+ requestId: message.id,
20736
+ threadId: String(threadId)
20737
+ });
20738
+ }
20739
+ }
20740
+ await sendToConnections({
20741
+ connectionIds: messageContext.connectionIds,
20742
+ connections,
20743
+ message,
20744
+ subscriptions,
20745
+ threadId: messageContext.threadId
20746
+ });
20747
+ }
20748
+ });
20749
+ function createConnection(connectionOptions) {
20750
+ const connectionId = connectionOptions.connectionId ?? nextConnectionId++;
20751
+ const session = CodexAppServerConnectionSessionState.fromSnapshot(
20752
+ connectionOptions.snapshot
20753
+ );
20754
+ const processor = runtime.createMessageProcessor({
20755
+ connectionId,
20756
+ session
20757
+ });
20758
+ connections.set(connectionId, {
20759
+ context: connectionOptions.context,
20760
+ send: connectionOptions.send
20761
+ });
20762
+ if (session.initialized()) {
20763
+ runtime.connectionInitialized(connectionId);
20764
+ }
20765
+ async function persistSnapshot() {
20766
+ await connectionOptions.onSnapshot?.(processor.session.snapshot());
20767
+ }
20768
+ return {
20769
+ async accept(message) {
20770
+ const parsed = parseServerTransportPayload(message);
20771
+ if (parsed.type === "invalid") {
20772
+ await connectionOptions.send(
20773
+ serializeJsonRpcError(parsed.id, parsed.error)
20774
+ );
20775
+ return;
20776
+ }
20777
+ switch (parsed.message.type) {
20778
+ case "client_request": {
20779
+ subscribeFromClientRequest(
20780
+ subscriptions,
20781
+ connectionId,
20782
+ parsed.message.request
20783
+ );
20784
+ const outcome = await processor.processConnectionRequest(
20785
+ parsed.message.request,
20786
+ connectionOptions.context
20787
+ );
20788
+ if (parsed.message.request.method === "initialize") {
20789
+ runtime.connectionInitialized(connectionId);
20790
+ }
20791
+ if (outcome.type === "response") {
20792
+ subscribeFromResult(subscriptions, connectionId, outcome.result);
20793
+ }
20794
+ await persistSnapshot();
20795
+ return;
20796
+ }
20797
+ case "response": {
20798
+ const pending = await pendingServerRequests.take(
20799
+ parsed.message.response.id
20800
+ );
20801
+ if (pending) {
20802
+ await runtime.resolveServerRequest(
20803
+ {
20804
+ requestId: parsed.message.response.id,
20805
+ result: parsed.message.response.result,
20806
+ threadId: pending.threadId
20807
+ },
20808
+ connectionOptions.context
20809
+ );
20810
+ }
20811
+ await persistSnapshot();
20812
+ return;
20813
+ }
20814
+ case "error": {
20815
+ if (parsed.message.error.id !== null) {
20816
+ const pending = await pendingServerRequests.take(
20817
+ parsed.message.error.id
20818
+ );
20819
+ if (pending) {
20820
+ await runtime.rejectServerRequest(
20821
+ {
20822
+ error: parsed.message.error.error,
20823
+ requestId: parsed.message.error.id,
20824
+ threadId: pending.threadId
20825
+ },
20826
+ connectionOptions.context
20827
+ );
20828
+ }
20829
+ }
20830
+ await persistSnapshot();
20831
+ return;
20832
+ }
20833
+ case "client_notification":
20834
+ await persistSnapshot();
20835
+ return;
20836
+ }
20837
+ },
20838
+ async close() {
20839
+ connections.delete(connectionId);
20840
+ for (const connectionIds of subscriptions.values()) {
20841
+ connectionIds.delete(connectionId);
20842
+ }
20843
+ await processor.connectionClosed();
20844
+ await runtime.connectionClosed(connectionId);
20845
+ },
20846
+ connectionId,
20847
+ processor,
20848
+ snapshot() {
20849
+ return processor.session.snapshot();
20850
+ }
20851
+ };
20852
+ }
20853
+ return Object.assign(runtime, {
20854
+ createConnection,
20855
+ pendingServerRequests
20856
+ });
20857
+ }
20858
+ function createCodexAppServerConnection(appServer, options) {
20859
+ return appServer.createConnection(options);
20860
+ }
20861
+ async function maybeExecuteDynamicTool(input) {
20862
+ if (!isDynamicToolCallRequest(input.message) || !input.threadId) {
20863
+ return false;
20864
+ }
20865
+ const params = input.message.params;
20866
+ const tool = findDynamicTool(input.dynamicTools, params);
20867
+ if (!tool?.execute) {
20868
+ return false;
20869
+ }
20870
+ try {
20871
+ const result = await tool.execute(params.arguments, {
20872
+ callId: params.callId,
20873
+ context: input.context,
20874
+ namespace: params.namespace ?? null,
20875
+ params,
20876
+ threadId: params.threadId,
20877
+ tool: params.tool,
20878
+ turnId: params.turnId
20879
+ });
20880
+ await input.runtime.resolveServerRequest(
20881
+ {
20882
+ requestId: input.message.id,
20883
+ result,
20884
+ threadId: input.threadId
20885
+ },
20886
+ input.context
20887
+ );
20888
+ } catch (error) {
20889
+ await input.runtime.resolveServerRequest(
20890
+ {
20891
+ requestId: input.message.id,
20892
+ result: dynamicToolResponse.error(
20893
+ error instanceof Error ? error.message : "Dynamic tool failed."
20894
+ ),
20895
+ threadId: input.threadId
20896
+ },
20897
+ input.context
20898
+ );
20899
+ }
20900
+ return true;
20901
+ }
20902
+ async function sendToConnections(input) {
20903
+ const payload = serializeOutgoingMessage(input.message);
20904
+ const recipients = recipientConnectionIds(input);
20905
+ await Promise.all(
20906
+ recipients.map(async (connectionId) => {
20907
+ const connection = input.connections.get(connectionId);
20908
+ if (connection) {
20909
+ await connection.send(payload);
20910
+ }
20911
+ })
20912
+ );
20913
+ }
20914
+ function recipientConnectionIds(input) {
20915
+ if (input.connectionIds?.length) {
20916
+ return input.connectionIds;
20917
+ }
20918
+ if (input.threadId) {
20919
+ return Array.from(input.subscriptions.get(String(input.threadId)) ?? []);
20920
+ }
20921
+ return Array.from(input.connections.keys());
20922
+ }
20923
+ function subscribeFromClientRequest(subscriptions, connectionId, request2) {
20924
+ const params = request2.params;
20925
+ const threadId = typeof params?.threadId === "string" ? params.threadId : typeof params?.thread_id === "string" ? params.thread_id : null;
20926
+ if (threadId) {
20927
+ subscribe(subscriptions, connectionId, threadId);
20928
+ }
20929
+ }
20930
+ function subscribeFromResult(subscriptions, connectionId, result) {
20931
+ const threadId = result?.thread?.id;
20932
+ if (typeof threadId === "string") {
20933
+ subscribe(subscriptions, connectionId, threadId);
20934
+ }
20935
+ }
20936
+ function subscribe(subscriptions, connectionId, threadId) {
20937
+ const connectionIds = subscriptions.get(threadId) ?? /* @__PURE__ */ new Set();
20938
+ connectionIds.add(connectionId);
20939
+ subscriptions.set(threadId, connectionIds);
20940
+ }
20941
+ function isServerRequest2(message) {
20942
+ return "method" in message && "id" in message;
20943
+ }
20944
+ function isDynamicToolCallRequest(message) {
20945
+ return isServerRequest2(message) && message.method === "item/tool/call";
20946
+ }
20947
+ function jsonRpcErrorFromUnknown2(error) {
20948
+ if (typeof error === "object" && error !== null && "code" in error && "message" in error && typeof error.code === "number" && typeof error.message === "string") {
20949
+ return error;
20950
+ }
20951
+ const nested = error?.error;
20952
+ if (typeof nested === "object" && nested !== null && "code" in nested && "message" in nested && typeof nested.code === "number" && typeof nested.message === "string") {
20953
+ return nested;
20954
+ }
20955
+ return {
20956
+ code: error instanceof CodexAppServerRequestError ? error.error.code : -32e3,
20957
+ message: error instanceof Error ? error.message : "Codex request failed."
20958
+ };
20959
+ }
20960
+
20961
+ export { AppServerSession, BaseInstructions, CodexAppServerClientTransportError, CodexAppServerConnectionSessionState, CodexAppServerMessageProcessor, CodexAppServerRequestError, CodexMcpConnectionManagerAdapter, ConnectionRpcGate, ConnectionRpcGateClosedError, EmptyMcpConnectionManager, InMemoryPendingServerRequestStore, InMemoryThreadStore, LiveThread, LocalThreadStore, McpRequestProcessor, ModelClientSession, OutgoingMessageSender, PendingAppServerRequests, RequestSerializationQueues, ResponsesClient, ResponsesWebsocketClient, ResponsesWebsocketConnection, SortDirection, StaticMcpConnectionManager, ThreadEventPersistenceMode, ThreadEventStore, ThreadHistoryBuilder, ThreadMemoryMode, ThreadRequestProcessor, ThreadScopedOutgoingMessageSender, ThreadSortKey, TurnRequestProcessor, asThreadId, codexAppServerDeferredResponse, dynamicToolResponse as codexDynamicToolResponse, createCodexAppServer, createCodexAppServerClient, createCodexAppServerConnection, createCodexAppServerRuntime, createModelClient, defineDynamicTool as defineCodexDynamicTool, defineDynamicToolset as defineCodexDynamicToolset, defineDynamicTool, defineDynamicToolset, deniedRequestPermissionsResponse, dynamicToolResponse, dynamicToolSpecFromDefinition, dynamicToolSpecsFromDefinitions, findDynamicTool, jsonRpcErrorFromUnknown2 as jsonRpcErrorFromUnknown, jsonRpcInternalError, jsonRpcInvalidRequestError, jsonRpcParseError, outgoingMessageToAppServerEvent, parseClientTransportPayload, parseCodexAppServerEvent, parseJsonRpcMessage, parseJsonRpcTransportPayload, parseServerTransportPayload, queuedOutgoingMessage, requestMethodName, requestSerializationQueueKeyFromScope, requestTyped, serializeJsonRpcError, serializeJsonRpcResponse, serializeOutgoingMessage, serverNotificationRequiresDelivery, serverNotificationThreadTarget, serverRequestThreadId, setRenderedThreadConnectionStatus, threadEventSnapshotHasStarted, validateDynamicToolDefinitions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jrkropp/codex-js",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Unofficial TypeScript Codex runtime for building Codex-backed web apps.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",