@jrkropp/codex-js 0.1.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 +5 -0
- package/LICENSE +201 -0
- package/NOTICE +29 -0
- package/README.md +659 -0
- package/dist/DynamicToolCallResponse-D2OVpa4p.d.ts +8 -0
- package/dist/ToolRequestUserInputResponse-Bxjlpgho.d.ts +17 -0
- package/dist/chat-runtime-CMli5dzJ.d.ts +748 -0
- package/dist/chunk-FFASNDXU.js +9383 -0
- package/dist/chunk-FFASNDXU.js.map +1 -0
- package/dist/chunk-FN3SWHRH.js +934 -0
- package/dist/chunk-FN3SWHRH.js.map +1 -0
- package/dist/chunk-TZBLUZ2X.js +18640 -0
- package/dist/chunk-TZBLUZ2X.js.map +1 -0
- package/dist/chunk-ZX5OIIJX.js +3 -0
- package/dist/chunk-ZX5OIIJX.js.map +1 -0
- package/dist/client/index.d.ts +111 -0
- package/dist/client/index.js +4 -0
- package/dist/client/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.d.ts +74 -0
- package/dist/react/index.js +5 -0
- package/dist/react/index.js.map +1 -0
- package/dist/remote-DMPfepa9.d.ts +19 -0
- package/dist/server/index.d.ts +1999 -0
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -0
- package/dist/session-BO6EZNK7.d.ts +1829 -0
- package/dist/shadcn/index.d.ts +68 -0
- package/dist/shadcn/index.js +334 -0
- package/dist/shadcn/index.js.map +1 -0
- package/dist/sidebar-DT2XoitN.d.ts +87 -0
- package/dist/store-H2cQxdpe.d.ts +20 -0
- package/dist/styles.css +1 -0
- package/dist/testing/index.d.ts +91 -0
- package/dist/testing/index.js +3 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/thread_event_store-BIS0qzhi.d.ts +3843 -0
- package/package.json +89 -0
package/README.md
ADDED
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
# codex-js
|
|
2
|
+
|
|
3
|
+
`@jrkropp/codex-js` is a Codex runtime and UI kit for
|
|
4
|
+
TypeScript React applications.
|
|
5
|
+
|
|
6
|
+
The design is intentionally upstream-first. `src/upstream/codex-rs` is directly
|
|
7
|
+
based on Codex source code, and `src/upstream/t3code` is directly based on T3
|
|
8
|
+
code. Keep their file names, folder structure, lifecycle boundaries, protocol
|
|
9
|
+
names, and component ownership as close to upstream as practical. That upstream-shaped source layout is the maintenance strategy.
|
|
10
|
+
|
|
11
|
+
Codex source and T3 code remain the references for runtime behavior, file
|
|
12
|
+
boundaries, naming, and interaction patterns.
|
|
13
|
+
|
|
14
|
+
Ownership is deliberately split: Codex owns runtime truth, T3 owns interaction
|
|
15
|
+
quality, and the consuming app owns product meaning. Host applications supply
|
|
16
|
+
prompts, tools, routes, credentials, storage, and branded UI through package
|
|
17
|
+
contracts rather than by modifying package internals.
|
|
18
|
+
|
|
19
|
+
`codex-js` is an unofficial TypeScript port. It is not affiliated with,
|
|
20
|
+
endorsed by, or sponsored by OpenAI.
|
|
21
|
+
|
|
22
|
+
## Package Structure
|
|
23
|
+
|
|
24
|
+
| Segment | Purpose | Source of Truth |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| `src/upstream/codex-rs` | Codex runtime, protocol, thread store, tools, model transport, and app-server protocol primitives. | `.reference/codex/codex-rs` |
|
|
27
|
+
| `src/upstream/t3code` | T3Chat web UI primitives: composer, timeline, model picker, image previews, and chat helpers. | `.reference/t3code` |
|
|
28
|
+
| `src/runtime` | Package-owned Codex lifecycle contracts, store and reader boundaries, app-server boundary, and route-neutral thread state. | Package-owned |
|
|
29
|
+
| `src/components` | Stable public React component surface built from the T3 upstream source. | Package-owned |
|
|
30
|
+
| `src/shadcn` | Package-owned standard shadcn primitives used for optional chat layout composition. | Package-owned |
|
|
31
|
+
| `src/hooks` | Stable public React hooks for binding a configured runtime to application UI. | Package-owned |
|
|
32
|
+
| `src/testing` | Package and consumer testing utilities. | Package-owned |
|
|
33
|
+
|
|
34
|
+
## Import Surfaces
|
|
35
|
+
|
|
36
|
+
Use the public package surfaces directly:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import {
|
|
40
|
+
createCodexAppServerClient,
|
|
41
|
+
type CodexAppServer,
|
|
42
|
+
} from "@jrkropp/codex-js/client";
|
|
43
|
+
import {
|
|
44
|
+
createCodexAppServerRuntime,
|
|
45
|
+
createModelClient,
|
|
46
|
+
InMemoryThreadStore,
|
|
47
|
+
LocalThreadStore,
|
|
48
|
+
AppServerSession,
|
|
49
|
+
CodexAppServerMessageProcessor,
|
|
50
|
+
parseServerTransportPayload,
|
|
51
|
+
serializeJsonRpcError,
|
|
52
|
+
serializeJsonRpcResponse,
|
|
53
|
+
type ThreadStore,
|
|
54
|
+
} from "@jrkropp/codex-js/server";
|
|
55
|
+
import {
|
|
56
|
+
CodexChat,
|
|
57
|
+
CodexChatLayout,
|
|
58
|
+
defaultCodexModel,
|
|
59
|
+
mentionToken,
|
|
60
|
+
useCodexChat,
|
|
61
|
+
type MentionBinding,
|
|
62
|
+
} from "@jrkropp/codex-js/react";
|
|
63
|
+
import {
|
|
64
|
+
SidebarContent,
|
|
65
|
+
SidebarGroup,
|
|
66
|
+
SidebarGroupContent,
|
|
67
|
+
SidebarGroupLabel,
|
|
68
|
+
} from "@jrkropp/codex-js/shadcn";
|
|
69
|
+
import {
|
|
70
|
+
type ThreadReader,
|
|
71
|
+
} from "@jrkropp/codex-js/react";
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The Codex and T3 upstream mirrors are internal implementation sources. Public
|
|
75
|
+
applications should depend on `client`, `server`, `react`, `shadcn`, and
|
|
76
|
+
`testing` instead of reaching into mirrored upstream folders.
|
|
77
|
+
|
|
78
|
+
Most app UI should not need the upstream T3 path. The `components` surface
|
|
79
|
+
exposes the reusable interaction primitives a host app naturally needs:
|
|
80
|
+
composer draft state, mention encoding, model and reasoning-effort options,
|
|
81
|
+
image attachment helpers, realtime-control state, and the polished `CodexChat`
|
|
82
|
+
shell. Direct T3 imports are reserved for advanced adapter work.
|
|
83
|
+
|
|
84
|
+
The `shadcn` surface exposes package-owned radix-nova shadcn primitives for
|
|
85
|
+
apps that want the same optional workspace shell building blocks. It is a layout
|
|
86
|
+
primitive surface, not a required chat dependency.
|
|
87
|
+
|
|
88
|
+
The package root remains intentionally small. It exposes the plug-and-play chat
|
|
89
|
+
and optional layout entrypoints only; runtime, hooks, components, shadcn, Codex
|
|
90
|
+
mirrors, and T3 mirrors stay on explicit subpaths.
|
|
91
|
+
|
|
92
|
+
## Store Model
|
|
93
|
+
|
|
94
|
+
`ThreadStore` is the Codex storage boundary. A configured store represents the
|
|
95
|
+
host application's storage scope: a local folder, database, remote service, or
|
|
96
|
+
Durable Object.
|
|
97
|
+
|
|
98
|
+
`ThreadReader` is the narrow read view over a configured thread store for
|
|
99
|
+
store-only and headless integrations. Plug-and-play chat hydration uses
|
|
100
|
+
`CodexAppServer.threadResume`, which presents stored Codex history as a
|
|
101
|
+
generated app-server `Thread` snapshot.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
const store = InMemoryThreadStore.forId("dev");
|
|
105
|
+
const threadReader: ThreadReader = store;
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`InMemoryThreadStore` is the standard test and example implementation.
|
|
109
|
+
`LocalThreadStore` is the standard local-store shape for Node and desktop
|
|
110
|
+
integrations. Cloudflare Durable Objects, SQL, and remote services implement the
|
|
111
|
+
same `ThreadStore` contract in application code.
|
|
112
|
+
|
|
113
|
+
The runtime receives configured store, reader, and app-server boundaries and runs
|
|
114
|
+
Codex-shaped thread lifecycle logic against them. Codex App Server runs Codex,
|
|
115
|
+
the store remembers Codex, and the UI renders generated app-server snapshots and
|
|
116
|
+
live events. `CodexAppServer` is how UI reaches Codex App Server.
|
|
117
|
+
|
|
118
|
+
## App Server Model
|
|
119
|
+
|
|
120
|
+
`CodexAppServer` is the UI-facing Codex App Server boundary. It mirrors Codex's
|
|
121
|
+
generated typed request, response, notification, and server-request protocol.
|
|
122
|
+
The protocol types come from `src/upstream/codex-rs/app-server-protocol/schema/typescript`.
|
|
123
|
+
Package-owned code wraps that generated protocol; it does not redefine it.
|
|
124
|
+
`AppServerSession` owns request-id lifecycle and typed helpers such as
|
|
125
|
+
`initialize`, `threadStart`, `threadResume`, `threadList`, `threadRead`,
|
|
126
|
+
`threadNameSet`, `turnStart`, `turnSteer`, `turnInterrupt`, and
|
|
127
|
+
`threadCompactStart`. `PendingAppServerRequests` owns request-id pending UI
|
|
128
|
+
state for server requests.
|
|
129
|
+
|
|
130
|
+
The package mirrors Codex's app-server control plane. `initialize` is the first
|
|
131
|
+
request on a connection and records client identity, experimental API
|
|
132
|
+
capability, and notification opt-outs. `CodexAppServerMessageProcessor` then
|
|
133
|
+
checks initialization, gates experimental requests, derives request
|
|
134
|
+
serialization from generated protocol metadata, and dispatches to Codex-shaped
|
|
135
|
+
request processors. `RequestSerializationQueues` gives same-scope requests FIFO
|
|
136
|
+
ordering, while `ConnectionRpcGate` stops late work after connection shutdown.
|
|
137
|
+
|
|
138
|
+
The app-server implementation owns platform details such as HTTP routes,
|
|
139
|
+
WebSocket tickets, credentials, Durable Objects, and hosted service URLs. Those
|
|
140
|
+
details stay outside the package.
|
|
141
|
+
|
|
142
|
+
`CodexAppServerMessageProcessor` is the server-side processor for a single
|
|
143
|
+
app-server connection. It dispatches generated `ClientRequest` values after
|
|
144
|
+
`initialize`, sends JSON-RPC responses through `OutgoingMessageSender`, and owns
|
|
145
|
+
protocol dispatch, initialize state, serialization, and connection gating. The
|
|
146
|
+
application implements Codex behavior: credentials, prompts, tools, storage
|
|
147
|
+
placement, runtime execution, and deployment.
|
|
148
|
+
|
|
149
|
+
`createCodexAppServerRuntime` is the standard runtime doorway. It implements
|
|
150
|
+
the Codex App Server method handlers from a configured `ThreadStore`, host-owned
|
|
151
|
+
model client creation, and app-owned protocol event delivery. The package runs
|
|
152
|
+
Codex session lifecycle; the application owns credentials, tools, prompts,
|
|
153
|
+
storage, event delivery, and deployment.
|
|
154
|
+
|
|
155
|
+
Core `EventMsg` values remain runtime and storage internals. Worker, local, and
|
|
156
|
+
hosted app-server implementations emit generated `ServerNotification` and
|
|
157
|
+
`ServerRequest` values on browser-facing streams. The package runtime owns live
|
|
158
|
+
protocol emission, request-id tracking, and server-request resolution ordering;
|
|
159
|
+
host applications deliver the resulting `AppServerEvent` values. `thread/resume`
|
|
160
|
+
returns a generated `ThreadResumeResponse`, so initial hydration and live
|
|
161
|
+
streaming share the same app-server model.
|
|
162
|
+
|
|
163
|
+
Runtime method handling follows Codex's request processor boundary:
|
|
164
|
+
`ThreadRequestProcessor` owns thread start, resume, list, read, name, archive,
|
|
165
|
+
unarchive, metadata update, and compaction, while `TurnRequestProcessor` owns
|
|
166
|
+
turn start, steer, and interrupt.
|
|
167
|
+
|
|
168
|
+
React chat state reduces the generated app-server protocol directly.
|
|
169
|
+
`ThreadEventStore` owns the protocol-native thread snapshot, active turns,
|
|
170
|
+
pending server requests, warnings, errors, and connection state. T3 rendering
|
|
171
|
+
adapters live in `src/components` and turn that snapshot plus lifecycle UI state
|
|
172
|
+
into `CodexChatRenderState`; they do not reconstruct core `EventMsg` values
|
|
173
|
+
after the app-server boundary.
|
|
174
|
+
|
|
175
|
+
## Model Transport Model
|
|
176
|
+
|
|
177
|
+
The package mirrors Codex's model transport split. `core/src/client.ts` owns the
|
|
178
|
+
session-scoped `ModelClient` and turn-scoped `ModelClientSession`.
|
|
179
|
+
`codex-api/src/endpoint/responses_websocket.ts` owns Responses-over-WebSocket,
|
|
180
|
+
and `codex-api/src/endpoint/responses.ts` owns HTTP/SSE as Codex's fallback
|
|
181
|
+
transport.
|
|
182
|
+
|
|
183
|
+
There are two separate WebSocket layers:
|
|
184
|
+
|
|
185
|
+
- The browser-facing Codex app-server WebSocket carries generated JSON-RPC
|
|
186
|
+
`ClientRequest`, response, notification, and server-request messages between
|
|
187
|
+
UI and host runtime.
|
|
188
|
+
- The server-side OpenAI Responses WebSocket carries `response.create`,
|
|
189
|
+
`response.processed`, and model stream events between the host runtime and
|
|
190
|
+
OpenAI.
|
|
191
|
+
|
|
192
|
+
`ModelClient` prefers Responses WebSocket when the provider supports it. A
|
|
193
|
+
`ModelClientSession` opens the socket lazily for a turn, captures
|
|
194
|
+
`x-codex-turn-state`, prewarms with `generate: false`, reuses the connection
|
|
195
|
+
within the turn, sends incremental `previous_response_id` payloads when
|
|
196
|
+
possible, and switches the whole session to HTTP/SSE fallback when the
|
|
197
|
+
WebSocket path is unsupported or unhealthy. The consuming app still owns
|
|
198
|
+
credential policy, provider selection, prompts, dynamic tools, and storage.
|
|
199
|
+
|
|
200
|
+
## Usage
|
|
201
|
+
|
|
202
|
+
### Plug-and-play
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
const appServer = createCodexAppServerClient({
|
|
206
|
+
url: async () => {
|
|
207
|
+
const { ticket } = await fetch("/api/codex/app-server/ticket", {
|
|
208
|
+
method: "POST",
|
|
209
|
+
}).then((response) => response.json());
|
|
210
|
+
const url = new URL("/api/codex/app-server", window.location.origin);
|
|
211
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
212
|
+
url.searchParams.set("ticket", ticket);
|
|
213
|
+
return url.toString();
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The client opens one Codex app-server WebSocket, sends `initialize`, sends all
|
|
219
|
+
generated `ClientRequest` values on that connection, resolves JSON-RPC responses
|
|
220
|
+
by request id, and receives generated `ServerNotification` and `ServerRequest`
|
|
221
|
+
messages on the same socket.
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
<CodexChat
|
|
225
|
+
appServer={appServer}
|
|
226
|
+
threadId={threadId}
|
|
227
|
+
/>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`CodexChat` renders the default Codex/T3 chat experience. It owns lifecycle
|
|
231
|
+
hydration, live events, optimistic rows, composer handoff, pending server
|
|
232
|
+
requests, and scroll coordination.
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
<CodexChat
|
|
236
|
+
appServer={appServer}
|
|
237
|
+
threadId={threadId}
|
|
238
|
+
title="Support Assistant"
|
|
239
|
+
buildTurnStartParams={({ threadId, input, model, effort, clientMessageId }) => ({
|
|
240
|
+
threadId,
|
|
241
|
+
input,
|
|
242
|
+
model,
|
|
243
|
+
effort,
|
|
244
|
+
clientMessageId,
|
|
245
|
+
cwd: "/workspace",
|
|
246
|
+
})}
|
|
247
|
+
/>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Optional Sidebar Layout
|
|
251
|
+
|
|
252
|
+
`CodexChatLayout` wraps any chat node in a standard shadcn `SidebarProvider`
|
|
253
|
+
only when `sidebar` is provided. Without `sidebar`, it returns the children
|
|
254
|
+
unchanged.
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
<CodexChatLayout
|
|
258
|
+
defaultOpen
|
|
259
|
+
sidebar={
|
|
260
|
+
<SidebarContent>
|
|
261
|
+
<SidebarGroup>
|
|
262
|
+
<SidebarGroupLabel>Threads</SidebarGroupLabel>
|
|
263
|
+
<SidebarGroupContent>{threadList}</SidebarGroupContent>
|
|
264
|
+
</SidebarGroup>
|
|
265
|
+
</SidebarContent>
|
|
266
|
+
}
|
|
267
|
+
>
|
|
268
|
+
<CodexChat
|
|
269
|
+
appServer={appServer}
|
|
270
|
+
threadId={threadId}
|
|
271
|
+
/>
|
|
272
|
+
</CodexChatLayout>
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
The sidebar has no product meaning in the package. A host app decides whether
|
|
276
|
+
the sidebar contains threads, files, tools, plan details, context, or app
|
|
277
|
+
navigation.
|
|
278
|
+
|
|
279
|
+
### Server Handler
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
const runtime = createCodexAppServerRuntime({
|
|
283
|
+
store,
|
|
284
|
+
createModelClient({ context, threadId }) {
|
|
285
|
+
return createModelClient({
|
|
286
|
+
apiKey: context.credentials.openaiApiKey,
|
|
287
|
+
installationId: context.installationId,
|
|
288
|
+
sessionId: String(threadId),
|
|
289
|
+
threadId,
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
modelClientCacheKey({ context, threadId }) {
|
|
293
|
+
return `${threadId}:${context.credentials.providerKeyHash}`;
|
|
294
|
+
},
|
|
295
|
+
sendOutgoingMessage(event, { threadId }) {
|
|
296
|
+
publishCodexEvent(threadId, event);
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const processorsByConnection = new Map<number, ReturnType<typeof runtime.createMessageProcessor>>();
|
|
301
|
+
|
|
302
|
+
export function acceptAppServerSocket(socket: WebSocket, connectionId: number) {
|
|
303
|
+
const processor =
|
|
304
|
+
processorsByConnection.get(connectionId) ??
|
|
305
|
+
runtime.createMessageProcessor({ connectionId });
|
|
306
|
+
processorsByConnection.set(connectionId, processor);
|
|
307
|
+
|
|
308
|
+
socket.addEventListener("message", async (event) => {
|
|
309
|
+
const parsed = parseServerTransportPayload(event.data);
|
|
310
|
+
if (parsed.type === "invalid") {
|
|
311
|
+
socket.send(serializeJsonRpcError(parsed.id, parsed.error));
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (parsed.message.type !== "client_request") {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
const result = await processor.processClientRequest(parsed.message.request, {
|
|
319
|
+
user,
|
|
320
|
+
credentials,
|
|
321
|
+
});
|
|
322
|
+
socket.send(serializeJsonRpcResponse(parsed.message.request.id, result));
|
|
323
|
+
} catch (error) {
|
|
324
|
+
socket.send(serializeJsonRpcError(
|
|
325
|
+
parsed.message.request.id,
|
|
326
|
+
errorToJsonRpcError(error),
|
|
327
|
+
));
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
The runtime implements Codex App Server methods from Codex primitives. The
|
|
334
|
+
handler accepts generated Codex `ClientRequest` messages, requires `initialize`
|
|
335
|
+
before normal methods, and returns generated method responses as JSON-RPC
|
|
336
|
+
response messages. Neither layer knows the host framework, route layout,
|
|
337
|
+
deployment platform, credentials, or model provider policy.
|
|
338
|
+
|
|
339
|
+
Production integrations use the same shape. A host app configures the runtime
|
|
340
|
+
with its `ThreadStore`, credential/model-client factory, prompts, tools,
|
|
341
|
+
app-server ticket route, and WebSocket transport adapter; it does not reimplement
|
|
342
|
+
Codex session lifecycle in route handlers.
|
|
343
|
+
|
|
344
|
+
### App-Owned Dynamic Tools
|
|
345
|
+
|
|
346
|
+
Dynamic tools are app-owned capabilities. The package registers them with the
|
|
347
|
+
Codex `ToolRouter`, exposes visible tools in the model request, exposes deferred
|
|
348
|
+
tools through `tool_search`, emits generated `ServerRequest` values when the
|
|
349
|
+
model calls them, and reinjects results into the Codex turn loop.
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
const billingTools = [
|
|
353
|
+
{
|
|
354
|
+
namespace: "billing",
|
|
355
|
+
name: "lookup_invoice",
|
|
356
|
+
description: "Look up an invoice by invoice id.",
|
|
357
|
+
input_schema: {
|
|
358
|
+
type: "object",
|
|
359
|
+
properties: {
|
|
360
|
+
invoiceId: { type: "string" },
|
|
361
|
+
},
|
|
362
|
+
required: ["invoiceId"],
|
|
363
|
+
additionalProperties: false,
|
|
364
|
+
},
|
|
365
|
+
defer_loading: false,
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
namespace: "billing",
|
|
369
|
+
name: "refund_invoice",
|
|
370
|
+
description: "Refund an invoice after checking policy constraints.",
|
|
371
|
+
input_schema: {
|
|
372
|
+
type: "object",
|
|
373
|
+
properties: {
|
|
374
|
+
invoiceId: { type: "string" },
|
|
375
|
+
reason: { type: "string" },
|
|
376
|
+
},
|
|
377
|
+
required: ["invoiceId", "reason"],
|
|
378
|
+
additionalProperties: false,
|
|
379
|
+
},
|
|
380
|
+
defer_loading: true,
|
|
381
|
+
},
|
|
382
|
+
];
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
`defer_loading: false` makes a tool visible on ordinary model requests.
|
|
386
|
+
`defer_loading: true` keeps a tool registered while making it discoverable
|
|
387
|
+
through `tool_search`, which uses BM25 over tool names, namespaces,
|
|
388
|
+
descriptions, and schema property names. Applications execute dynamic tools and
|
|
389
|
+
resolve `item/tool/call` by generated `RequestId`; Codex uses the model
|
|
390
|
+
`call_id` internally to attach the result to the original tool call.
|
|
391
|
+
|
|
392
|
+
`request_user_input` follows the same boundary. Codex exposes it as a core tool,
|
|
393
|
+
emits `item/tool/requestUserInput` as a generated `ServerRequest`, and the T3
|
|
394
|
+
composer renders the pending questions. Apps resolve the prompt by generated
|
|
395
|
+
`RequestId`; `params.itemId` remains the model/tool item identity.
|
|
396
|
+
|
|
397
|
+
The full tool model is documented in
|
|
398
|
+
[`docs/start-here/03-tool-calling.md`](./docs/start-here/03-tool-calling.md).
|
|
399
|
+
|
|
400
|
+
### Hosted Web Search
|
|
401
|
+
|
|
402
|
+
Web search mirrors Codex exactly. It is a hosted Responses API tool controlled
|
|
403
|
+
by top-level config, not an app-owned dynamic tool or approval request.
|
|
404
|
+
|
|
405
|
+
```toml
|
|
406
|
+
web_search = "cached" # default effective mode
|
|
407
|
+
# web_search = "live" # allow live external web access
|
|
408
|
+
# web_search = "disabled"
|
|
409
|
+
|
|
410
|
+
[tools.web_search]
|
|
411
|
+
context_size = "medium"
|
|
412
|
+
allowed_domains = ["openai.com"]
|
|
413
|
+
|
|
414
|
+
[tools.web_search.location]
|
|
415
|
+
country = "US"
|
|
416
|
+
region = "WA"
|
|
417
|
+
city = "Seattle"
|
|
418
|
+
timezone = "America/Los_Angeles"
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
`cached` sends the hosted `web_search` tool with
|
|
422
|
+
`external_web_access: false`. `live` sends the same tool with
|
|
423
|
+
`external_web_access: true`. `disabled` omits it. `[tools.web_search]` only
|
|
424
|
+
configures context size, domain filters, and approximate location; legacy
|
|
425
|
+
boolean values are accepted as no-op config payloads. Host CLI switches like
|
|
426
|
+
`--search` should map to top-level `web_search = "live"`.
|
|
427
|
+
|
|
428
|
+
### Plan Mode
|
|
429
|
+
|
|
430
|
+
Plan Mode is Codex collaboration state rendered through T3. Codex injects Plan
|
|
431
|
+
Mode instructions through `collaborationMode`, exposes `request_user_input`
|
|
432
|
+
when the model needs structured feedback, and turns `<proposed_plan>` blocks
|
|
433
|
+
into generated `Plan` thread items. T3 renders those results as composer
|
|
434
|
+
questions and proposed-plan cards.
|
|
435
|
+
|
|
436
|
+
```tsx
|
|
437
|
+
<CodexChat
|
|
438
|
+
appServer={appServer}
|
|
439
|
+
threadId={threadId}
|
|
440
|
+
showInteractionModeToggle
|
|
441
|
+
defaultInteractionMode="plan"
|
|
442
|
+
/>
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Applications can customize plan actions without changing package source:
|
|
446
|
+
|
|
447
|
+
```tsx
|
|
448
|
+
<CodexChat
|
|
449
|
+
appServer={appServer}
|
|
450
|
+
threadId={threadId}
|
|
451
|
+
showInteractionModeToggle
|
|
452
|
+
onImplementProposedPlan={(plan) => {
|
|
453
|
+
queueImplementation(plan.planMarkdown);
|
|
454
|
+
}}
|
|
455
|
+
/>
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
The default behavior follows T3: when a proposed plan is active in Plan mode,
|
|
459
|
+
submitting an empty composer sends `PLEASE IMPLEMENT THIS PLAN:` with the plan
|
|
460
|
+
markdown and switches the next turn back to Build mode.
|
|
461
|
+
|
|
462
|
+
### Composer Commands
|
|
463
|
+
|
|
464
|
+
The default composer is an internal T3-shaped port. `/` opens command
|
|
465
|
+
suggestions, `@` opens mention suggestions, `$` opens skill suggestions, arrow
|
|
466
|
+
keys move the active row, and Enter or Tab selects it. Built-in commands stay
|
|
467
|
+
T3-shaped: `/model`, `/plan`, and `/default`.
|
|
468
|
+
|
|
469
|
+
Applications add product commands and skills through component props instead of
|
|
470
|
+
editing upstream source:
|
|
471
|
+
|
|
472
|
+
```tsx
|
|
473
|
+
<CodexChat
|
|
474
|
+
appServer={appServer}
|
|
475
|
+
threadId={threadId}
|
|
476
|
+
composerCommands={[
|
|
477
|
+
{
|
|
478
|
+
name: "billing",
|
|
479
|
+
label: "/billing",
|
|
480
|
+
description: "Insert a billing-tools prompt",
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
name: "realtime",
|
|
484
|
+
label: "/realtime",
|
|
485
|
+
description: "Start realtime voice when configured",
|
|
486
|
+
disabled: true,
|
|
487
|
+
unavailableReason: "Realtime is not configured for this chat.",
|
|
488
|
+
},
|
|
489
|
+
]}
|
|
490
|
+
composerSkills={[
|
|
491
|
+
{
|
|
492
|
+
name: "billing-tools",
|
|
493
|
+
description: "Use the app-owned billing dynamic tools",
|
|
494
|
+
},
|
|
495
|
+
]}
|
|
496
|
+
onCommand={(command) => {
|
|
497
|
+
if (command === "billing") {
|
|
498
|
+
insertBillingPrompt();
|
|
499
|
+
}
|
|
500
|
+
}}
|
|
501
|
+
/>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
Unsupported visible controls stay visible and deterministic: pass `disabled`
|
|
505
|
+
and `unavailableReason` so the composer can explain why an app-owned action is
|
|
506
|
+
not available in the current host.
|
|
507
|
+
|
|
508
|
+
A runnable minimal version of this full path lives in
|
|
509
|
+
`examples/minimal-app-server`. It uses `InMemoryThreadStore`, the runtime
|
|
510
|
+
doorway, the handler doorway, the generic app-server client, and `CodexChat` to
|
|
511
|
+
prove the public package APIs compose without product-specific host code, Cloudflare, Durable
|
|
512
|
+
Objects, React Router, upstream Codex imports, or upstream T3 imports.
|
|
513
|
+
|
|
514
|
+
### Polished Shell
|
|
515
|
+
|
|
516
|
+
```tsx
|
|
517
|
+
<CodexChatView
|
|
518
|
+
lifecycle={{
|
|
519
|
+
appServer,
|
|
520
|
+
threadId,
|
|
521
|
+
buildTurnStartParams,
|
|
522
|
+
}}
|
|
523
|
+
title={title}
|
|
524
|
+
subtitle={subtitle}
|
|
525
|
+
actions={actions}
|
|
526
|
+
/>
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### Headless
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
const chat = useCodexChat({
|
|
533
|
+
threadId,
|
|
534
|
+
appServer,
|
|
535
|
+
threadReader,
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
await chat.sendMessage({ text: "Summarize this thread." });
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### Composed React
|
|
542
|
+
|
|
543
|
+
```tsx
|
|
544
|
+
<CodexChatProvider appServer={appServer} threadId={threadId} threadReader={threadReader}>
|
|
545
|
+
<CodexThread>
|
|
546
|
+
<CodexMessages />
|
|
547
|
+
<CodexComposer />
|
|
548
|
+
</CodexThread>
|
|
549
|
+
</CodexChatProvider>
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Presentation Extension
|
|
553
|
+
|
|
554
|
+
```tsx
|
|
555
|
+
<CodexChat
|
|
556
|
+
appServer={appServer}
|
|
557
|
+
threadId={threadId}
|
|
558
|
+
title="Acme"
|
|
559
|
+
renderPendingRequest={({ request, defaultNode, resolve, reject }) => {
|
|
560
|
+
if (request.kind === "dynamicToolCall") {
|
|
561
|
+
return (
|
|
562
|
+
<ProductToolPanel
|
|
563
|
+
request={request}
|
|
564
|
+
onResolve={resolve}
|
|
565
|
+
onReject={reject}
|
|
566
|
+
/>
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
return defaultNode;
|
|
570
|
+
}}
|
|
571
|
+
/>
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
The package owns the default Codex/T3 experience. Applications own product
|
|
575
|
+
meaning and extend presentation through slots, not package source edits.
|
|
576
|
+
|
|
577
|
+
## What You Implement
|
|
578
|
+
|
|
579
|
+
Applications provide the Codex App Server boundary and product policy:
|
|
580
|
+
|
|
581
|
+
- `CodexAppServer`: typed `thread/start`, `thread/resume`, `turn/start`,
|
|
582
|
+
`turn/steer`, `turn/interrupt`, request-id resolution, and app-server events.
|
|
583
|
+
- `createCodexAppServerRuntime`: the standard runtime harness when the host
|
|
584
|
+
wants package-owned Codex session lifecycle over an app-owned `ThreadStore`,
|
|
585
|
+
model client factory, and event sink.
|
|
586
|
+
- `createCodexAppServerClient`: the standard protocol client when the host
|
|
587
|
+
exposes a Codex app-server WebSocket plus an app-owned ticket/auth route.
|
|
588
|
+
- `CodexAppServerMessageProcessor`: the standard protocol dispatcher when the
|
|
589
|
+
host accepts generated `ClientRequest` messages and routes them to app-owned
|
|
590
|
+
runtime operations.
|
|
591
|
+
- `ThreadReader`: optional store-only read view for headless or fallback paths.
|
|
592
|
+
- Parameter builders: optional prompt, tool, cwd, model, sandbox, approval, and
|
|
593
|
+
metadata policy for thread and turn requests.
|
|
594
|
+
- Product UI: optional layout sidebar content, banners, actions, pending request
|
|
595
|
+
panels, mention sources, route titles, and product-specific renderers.
|
|
596
|
+
|
|
597
|
+
The server accepts generated `ClientRequest` JSON-RPC messages, returns typed
|
|
598
|
+
Codex responses, emits generated server notifications and server requests on the
|
|
599
|
+
same socket, and resolves or rejects `ServerRequest` values by `RequestId`.
|
|
600
|
+
|
|
601
|
+
Apps may compose the upstream-shaped T3 components directly when they need a full
|
|
602
|
+
T3-shaped shell. The package runtime owns chat lifecycle state; route code
|
|
603
|
+
should only resolve host context, configure the store and app server, and render
|
|
604
|
+
chat.
|
|
605
|
+
|
|
606
|
+
## Runtime Concepts
|
|
607
|
+
|
|
608
|
+
- `ThreadStore`: full Codex storage boundary.
|
|
609
|
+
- `ThreadReader`: narrow read view used by store-only and headless hooks.
|
|
610
|
+
- `LiveThread`: store-backed lifecycle for a single thread.
|
|
611
|
+
- `ClientRequest`: Codex App Server client-to-server method envelope.
|
|
612
|
+
- `ServerNotification`: Codex App Server server-to-client notification envelope.
|
|
613
|
+
- `ServerRequest`: Codex App Server server-to-client request envelope.
|
|
614
|
+
- `RequestId`: identity used to resolve or reject server requests.
|
|
615
|
+
- `AppServerSession`: typed request-id owner for Codex App Server lifecycle calls.
|
|
616
|
+
- `PendingAppServerRequests`: request-id indexed pending UI state for server requests.
|
|
617
|
+
- `ThreadEventStore`: protocol-native chat state store for generated app-server snapshots, notifications, and server requests.
|
|
618
|
+
- `ThreadEventSnapshot`: immutable protocol-native chat state consumed by hooks and components.
|
|
619
|
+
- `createCodexChatRenderState`: component-layer adapter from protocol state into `CodexChatRenderState` for the T3 timeline, composer, banners, and pending request slots.
|
|
620
|
+
- `Submission`: Codex core operation envelope used inside runtime/session internals.
|
|
621
|
+
- `Event` and `EventMsg`: Codex core runtime event flow used inside runtime and storage internals.
|
|
622
|
+
- `RenderedThreadState`: low-level Codex core projection for stored history and core tests.
|
|
623
|
+
- `CodexAppServer`: UI-facing Codex App Server boundary for typed requests, notifications, server requests, and request-id resolution.
|
|
624
|
+
|
|
625
|
+
Cloudflare, Durable Objects, browser storage, local files, routing, credentials,
|
|
626
|
+
tools, prompts, and product renderers are implementation choices in consuming
|
|
627
|
+
applications or documentation guides. They are not package primitives.
|
|
628
|
+
|
|
629
|
+
## Host App Integration
|
|
630
|
+
|
|
631
|
+
The consuming app owns product-specific behavior outside this package:
|
|
632
|
+
|
|
633
|
+
- branding
|
|
634
|
+
- prompts and developer instructions
|
|
635
|
+
- dynamic tools
|
|
636
|
+
- mention targets
|
|
637
|
+
- model defaults
|
|
638
|
+
- auth credentials
|
|
639
|
+
- route paths
|
|
640
|
+
- Durable Object binding names
|
|
641
|
+
|
|
642
|
+
Host applications pass those choices through runtime, store, app-server,
|
|
643
|
+
component, and hook contracts. Product-specific code must not be imported from
|
|
644
|
+
package internals.
|
|
645
|
+
|
|
646
|
+
## Copy-forward Workflow
|
|
647
|
+
|
|
648
|
+
To reuse this package in another application:
|
|
649
|
+
|
|
650
|
+
1. Copy `packages/codex-js` into the target repository.
|
|
651
|
+
2. Add path aliases for the exported package surfaces.
|
|
652
|
+
3. Implement host adapters outside the package.
|
|
653
|
+
4. Keep prompts, tools, route mounting, Durable Object bindings, and product
|
|
654
|
+
terminology outside the package.
|
|
655
|
+
5. Run the package typecheck and boundary tests before wiring the app UI.
|
|
656
|
+
|
|
657
|
+
To update from newer upstream sources, use `MIRROR_MAP.md` to locate the matching
|
|
658
|
+
Codex or T3 file, port upstream structure and terminology first, then apply only
|
|
659
|
+
documented TypeScript, browser, or Cloudflare adaptations.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EXPERIMENTAL. Captures a user's answer to a request_user_input question.
|
|
3
|
+
*/
|
|
4
|
+
type ToolRequestUserInputAnswer = {
|
|
5
|
+
answers: Array<string>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* EXPERIMENTAL. Response payload mapping question ids to answers.
|
|
10
|
+
*/
|
|
11
|
+
type ToolRequestUserInputResponse = {
|
|
12
|
+
answers: {
|
|
13
|
+
[key in string]?: ToolRequestUserInputAnswer;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type { ToolRequestUserInputResponse as T };
|