| tags | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
Codex App Server là giao thức giao tiếp hai chiều (bidirectional) mà OpenAI Codex sử dụng để cung cấp năng lực rich client integration — xác thực, lịch sử hội thoại, phê duyệt (approvals), và streaming sự kiện agent. Nếu bạn muốn nhúng Codex sâu vào sản phẩm của mình (tương tự cách VS Code extension tích hợp Codex), đây chính là interface bạn cần.
Thông tin dự án:
- Repository: openai/codex/codex-rs/app-server
- Tác giả: OpenAI
- Giấy phép: Open Source (xem Open Source page)
- Ngôn ngữ: Rust (codex-rs)
- Protocol: JSON-RPC 2.0 (không có header
"jsonrpc":"2.0"trên wire) - Transport: stdio (mặc định) | WebSocket (experimental)
- Docs: developers.openai.com/codex/app-server
graph TB
CENTER["🔌 Codex App Server"]
subgraph protocol ["📡 Protocol Layer"]
P1["JSON-RPC 2.0"]
P2["stdio (JSONL)"]
P3["WebSocket (experimental)"]
end
subgraph primitives ["🧱 Core Primitives"]
PR1["Thread — Conversation"]
PR2["Turn — User request + Agent work"]
PR3["Item — Input/Output unit"]
end
subgraph features ["⚡ Key Features"]
F1["Authentication (API Key / ChatGPT / External)"]
F2["Approval Workflows"]
F3["Streaming Events"]
F4["Skills & Apps (Connectors)"]
end
subgraph lifecycle ["🔄 Lifecycle"]
L1["initialize → thread/start"]
L2["turn/start → stream events"]
L3["turn/completed / interrupted"]
end
CENTER --- protocol
CENTER --- primitives
CENTER --- features
CENTER --- lifecycle
style CENTER fill:#10a37f,color:#fff,stroke:#0d8c6d,stroke-width:3px
style protocol fill:#1e272e,color:#dfe6e9,stroke:#0984e3,stroke-width:2px
style primitives fill:#1e272e,color:#dfe6e9,stroke:#00b894,stroke-width:2px
style features fill:#1e272e,color:#dfe6e9,stroke:#e17055,stroke-width:2px
style lifecycle fill:#1e272e,color:#dfe6e9,stroke:#fdcb6e,stroke-width:2px
style P1 fill:#0984e3,color:#fff,stroke:#74b9ff
style P2 fill:#0984e3,color:#fff,stroke:#74b9ff
style P3 fill:#0984e3,color:#fff,stroke:#74b9ff
style PR1 fill:#00b894,color:#fff,stroke:#55efc4
style PR2 fill:#00b894,color:#fff,stroke:#55efc4
style PR3 fill:#00b894,color:#fff,stroke:#55efc4
style F1 fill:#e17055,color:#fff,stroke:#fab1a0
style F2 fill:#e17055,color:#fff,stroke:#fab1a0
style F3 fill:#e17055,color:#fff,stroke:#fab1a0
style F4 fill:#e17055,color:#fff,stroke:#fab1a0
style L1 fill:#fdcb6e,color:#2d3436,stroke:#f9ca24
style L2 fill:#fdcb6e,color:#2d3436,stroke:#f9ca24
style L3 fill:#fdcb6e,color:#2d3436,stroke:#f9ca24
| Tiêu chí | Codex App Server | Codex SDK |
|---|---|---|
| Use case chính | Rich client integration (IDE, web app) | CI/CD automation, batch jobs |
| Giao tiếp | Bidirectional, streaming | Request-response |
| Approvals | ✅ Real-time approval workflows | ❌ Non-interactive |
| Conversation history | ✅ Thread/Turn model đầy đủ | ❌ Stateless |
| Authentication | API Key + ChatGPT OAuth + External tokens | API Key |
| Streaming events | ✅ item/started, deltas, completed | ❌ |
| Phù hợp cho | IDE extensions, web dashboards, custom agents | GitHub Actions, scripts, pipelines |
- MCP-inspired — Protocol lấy cảm hứng từ MCP (Model Context Protocol), dùng JSON-RPC 2.0 bidirectional.
- Transport-agnostic — stdio cho local integration, WebSocket cho networked clients.
- Streaming-first — Mọi thứ được stream qua notifications: text deltas, command output, file changes, reasoning.
- Approval-aware — Sandbox policies và approval workflows là first-class citizens, không phải afterthought.
- Open Source — Implementation nằm trong Codex GitHub repo, bạn có thể đọc và contribute.
Codex App Server sử dụng JSON-RPC 2.0 không có "jsonrpc":"2.0" header. Có 3 loại message:
graph LR
subgraph messages ["📨 Message Types"]
REQ["Request<br/>method + params + id"]
RES["Response<br/>id + result/error"]
NOT["Notification<br/>method + params (no id)"]
end
REQ -->|"Client → Server"| RES
NOT -->|"Server → Client"| CLIENT["Client"]
style REQ fill:#0984e3,color:#fff,stroke:#74b9ff
style RES fill:#00b894,color:#fff,stroke:#55efc4
style NOT fill:#e17055,color:#fff,stroke:#fab1a0
style CLIENT fill:#6c5ce7,color:#fff,stroke:#a29bfe
Request — Client gửi lên server, có id:
{ "method": "thread/start", "id": 10, "params": { "model": "gpt-5.1-codex" } }Response — Server trả về, echo id, có result hoặc error:
{ "id": 10, "result": { "thread": { "id": "thr_123" } } }{ "id": 10, "error": { "code": 123, "message": "Something went wrong" } }Notification — Server-initiated, không có id:
{ "method": "turn/started", "params": { "turn": { "id": "turn_456" } } }| Transport | Cú pháp | Đặc điểm |
|---|---|---|
| stdio | --listen stdio:// (mặc định) |
Newline-delimited JSON (JSONL), phù hợp local integration |
| WebSocket | --listen ws://IP:PORT (experimental) |
Một JSON-RPC message per WebSocket text frame, có bounded queues |
Lưu ý WebSocket: Khi request queue đầy, server trả error code
-32001với message"Server overloaded; retry later.". Client nên retry với exponential backoff + jitter.
Bạn có thể generate TypeScript hoặc JSON Schema từ CLI:
codex app-server generate-ts --out ./schemas
codex app-server generate-json-schema --out ./schemasSchema được tạo ra khớp chính xác với phiên bản Codex đang chạy.
graph TD
T["🧵 Thread<br/>(Conversation)"] --> TN1["Turn 1"]
T --> TN2["Turn 2"]
T --> TN3["Turn N..."]
TN1 --> I1["Item: userMessage"]
TN1 --> I2["Item: agentMessage"]
TN1 --> I3["Item: commandExecution"]
TN2 --> I4["Item: userMessage"]
TN2 --> I5["Item: fileChange"]
TN2 --> I6["Item: mcpToolCall"]
style T fill:#10a37f,color:#fff,stroke:#0d8c6d,stroke-width:2px
style TN1 fill:#0984e3,color:#fff,stroke:#74b9ff
style TN2 fill:#0984e3,color:#fff,stroke:#74b9ff
style TN3 fill:#0984e3,color:#fff,stroke:#74b9ff
style I1 fill:#636e72,color:#fff,stroke:#b2bec3
style I2 fill:#636e72,color:#fff,stroke:#b2bec3
style I3 fill:#636e72,color:#fff,stroke:#b2bec3
style I4 fill:#636e72,color:#fff,stroke:#b2bec3
style I5 fill:#636e72,color:#fff,stroke:#b2bec3
style I6 fill:#636e72,color:#fff,stroke:#b2bec3
| Primitive | Mô tả | Ví dụ |
|---|---|---|
| Thread | Một cuộc hội thoại giữa user và Codex agent | thr_123 — "Refactor auth module" |
| Turn | Một lượt request + toàn bộ agent work theo sau | User gửi "Run tests" → agent chạy, trả kết quả |
| Item | Đơn vị input/output nhỏ nhất | userMessage, agentMessage, commandExecution, fileChange, mcpToolCall... |
sequenceDiagram
participant C as Client
participant S as App Server
Note over C,S: 1. Khởi tạo kết nối
C->>S: initialize (clientInfo)
S-->>C: result (userAgent)
C->>S: initialized (notification)
Note over C,S: 2. Tạo/Resume Thread
C->>S: thread/start (model, cwd, sandbox...)
S-->>C: result (thread.id)
S-->>C: thread/started (notification)
Note over C,S: 3. Bắt đầu Turn
C->>S: turn/start (threadId, input)
S-->>C: result (turn.id, status: inProgress)
S-->>C: turn/started
Note over C,S: 4. Stream Events
S-->>C: item/started (agentMessage)
S-->>C: item/agentMessage/delta (text chunk)
S-->>C: item/agentMessage/delta (text chunk)
S-->>C: item/completed (agentMessage)
Note over C,S: 5. Hoàn thành
S-->>C: turn/completed (status: completed)
Mỗi connection phải gửi initialize trước khi gọi bất kỳ method nào, sau đó gửi initialized notification.
// Request
{
"method": "initialize",
"id": 0,
"params": {
"clientInfo": {
"name": "my_product",
"title": "My Product",
"version": "0.1.0"
}
}
}
// Notification (sau khi nhận response)
{ "method": "initialized", "params": {} }Lưu ý quan trọng:
- Request trước
initialize→ bị reject vớiNot initialized - Gọi
initializelần 2 → bị reject vớiAlready initialized clientInfo.namedùng để identify client cho OpenAI Compliance Logs Platform
Một số method/field yêu cầu opt-in vào experimental API:
{
"method": "initialize",
"id": 1,
"params": {
"clientInfo": { "name": "my_client", "title": "My Client", "version": "0.1.0" },
"capabilities": {
"experimentalApi": true,
"optOutNotificationMethods": [
"codex/event/session_configured",
"item/agentMessage/delta"
]
}
}
}// Tạo thread mới
{
"method": "thread/start", "id": 10,
"params": {
"model": "gpt-5.1-codex",
"cwd": "/Users/me/project",
"approvalPolicy": "never",
"sandbox": "workspaceWrite",
"personality": "friendly"
}
}
// Resume thread đã có
{
"method": "thread/resume", "id": 11,
"params": { "threadId": "thr_123", "personality": "friendly" }
}
// Fork thread (tạo bản sao lịch sử)
{
"method": "thread/fork", "id": 12,
"params": { "threadId": "thr_123" }
}{
"method": "turn/start", "id": 30,
"params": {
"threadId": "thr_123",
"input": [{ "type": "text", "text": "Run tests" }],
"cwd": "/Users/me/project",
"approvalPolicy": "unlessTrusted",
"sandboxPolicy": {
"type": "workspaceWrite",
"writableRoots": ["/Users/me/project"],
"networkAccess": true
},
"model": "gpt-5.1-codex",
"effort": "medium"
}
}Input types hỗ trợ:
| Type | Mô tả | Ví dụ |
|---|---|---|
text |
Văn bản | { "type": "text", "text": "Explain this diff" } |
image |
Ảnh từ URL | { "type": "image", "url": "https://.../design.png" } |
localImage |
Ảnh local | { "type": "localImage", "path": "/tmp/screenshot.png" } |
skill |
Invoke skill | { "type": "skill", "name": "skill-creator", "path": "..." } |
mention |
Invoke app | { "type": "mention", "name": "Demo App", "path": "app://demo-app" } |
{
"method": "turn/steer", "id": 32,
"params": {
"threadId": "thr_123",
"input": [{ "type": "text", "text": "Actually focus on failing tests first." }],
"expectedTurnId": "turn_456"
}
}
turn/steerkhông tạo turn mới, không emitturn/started, và không chấp nhận overrides (model, cwd, sandbox...).
{ "method": "turn/interrupt", "id": 31, "params": { "threadId": "thr_123", "turnId": "turn_456" } }Turn kết thúc với status: "interrupted".
import { spawn } from "node:child_process";
import readline from "node:readline";
// 1. Spawn app-server process
const proc = spawn("codex", ["app-server"], {
stdio: ["pipe", "pipe", "inherit"],
});
const rl = readline.createInterface({ input: proc.stdout });
// Helper: gửi JSON-RPC message
const send = (message: unknown) => {
proc.stdin.write(`${JSON.stringify(message)}\n`);
};
let threadId: string | null = null;
// 2. Lắng nghe messages từ server
rl.on("line", (line) => {
const msg = JSON.parse(line) as any;
console.log("server:", msg);
// Khi thread được tạo → bắt đầu turn
if (msg.id === 1 && msg.result?.thread?.id && !threadId) {
threadId = msg.result.thread.id;
send({
method: "turn/start",
id: 2,
params: {
threadId,
input: [{ type: "text", text: "Summarize this repo." }],
},
});
}
});
// 3. Handshake: initialize → initialized → thread/start
send({
method: "initialize",
id: 0,
params: {
clientInfo: {
name: "my_product",
title: "My Product",
version: "0.1.0",
},
},
});
send({ method: "initialized", params: {} });
send({ method: "thread/start", id: 1, params: { model: "gpt-5.1-codex" } });graph TD
A["Spawn / Connect"] --> B["send: initialize"]
B --> C["send: initialized"]
C --> D{"Mới hay resume?"}
D -->|Mới| E["send: thread/start"]
D -->|Resume| F["send: thread/resume"]
E --> G["Nhận thread.id"]
F --> G
G --> H["send: turn/start"]
H --> I["Stream: item/* events"]
I --> J{"Cần approval?"}
J -->|Có| K["Client trả approval decision"]
J -->|Không| L["Tiếp tục stream"]
K --> L
L --> M["turn/completed"]
M --> N{"Thêm turn?"}
N -->|Có| H
N -->|Không| O["Done / Cleanup"]
style A fill:#10a37f,color:#fff
style M fill:#00b894,color:#fff
style O fill:#6c5ce7,color:#fff
| Method | Mô tả |
|---|---|
thread/start |
Tạo thread mới, auto-subscribe events |
thread/resume |
Mở lại thread đã có |
thread/fork |
Fork thread thành thread mới (copy history) |
thread/read |
Đọc thread từ storage (không resume, không subscribe) |
thread/list |
Phân trang danh sách threads (hỗ trợ filters) |
thread/loaded/list |
List thread IDs đang nằm trong memory |
thread/archive |
Di chuyển thread log vào thư mục archived |
thread/unarchive |
Khôi phục thread từ archived |
thread/compact/start |
Trigger nén lịch sử hội thoại |
thread/rollback |
Xoá N turns cuối khỏi context |
{
"method": "thread/list", "id": 20,
"params": {
"cursor": null,
"limit": 25,
"sortKey": "created_at",
"modelProviders": ["openai"],
"sourceKinds": ["cli", "vscode", "appServer"],
"archived": false,
"cwd": "/Users/me/project"
}
}sourceKinds hỗ trợ: cli, vscode, exec, appServer, subAgent, subAgentReview, subAgentCompact, subAgentThreadSpawn, subAgentOther, unknown.
Khi nextCursor là null → bạn đã ở trang cuối.
{
"method": "thread/read", "id": 19,
"params": { "threadId": "thr_123", "includeTurns": true }
}Khác với
thread/resume: không load vào memory, không emitthread/started.
// Nén lịch sử (async, trả {} ngay)
{ "method": "thread/compact/start", "id": 25, "params": { "threadId": "thr_b" } }
// Xoá N turns cuối
{ "method": "thread/rollback", "id": 26, "params": { "threadId": "thr_b", "n": 3 } }graph TB
subgraph turn_events ["🔄 Turn Events"]
TE1["turn/started"]
TE2["turn/completed"]
TE3["turn/diff/updated"]
TE4["turn/plan/updated"]
end
subgraph item_lifecycle ["📦 Item Lifecycle"]
IL1["item/started"]
IL2["item/completed"]
end
subgraph item_deltas ["📝 Item Deltas (Streaming)"]
ID1["item/agentMessage/delta"]
ID2["item/plan/delta"]
ID3["item/reasoning/summaryTextDelta"]
ID4["item/commandExecution/outputDelta"]
ID5["item/fileChange/outputDelta"]
end
subgraph special ["🔔 Special"]
SP1["thread/tokenUsage/updated"]
SP2["app/list/updated"]
SP3["account/rateLimits/updated"]
end
style turn_events fill:#1e272e,color:#dfe6e9,stroke:#0984e3,stroke-width:2px
style item_lifecycle fill:#1e272e,color:#dfe6e9,stroke:#00b894,stroke-width:2px
style item_deltas fill:#1e272e,color:#dfe6e9,stroke:#e17055,stroke-width:2px
style special fill:#1e272e,color:#dfe6e9,stroke:#fdcb6e,stroke-width:2px
| Item Type | Mô tả | Fields chính |
|---|---|---|
userMessage |
Tin nhắn user | id, content (text/image/localImage) |
agentMessage |
Reply của agent | id, text |
plan |
Kế hoạch agent đề xuất | id, text |
reasoning |
Lý luận nội bộ | id, summary, content |
commandExecution |
Chạy command | id, command, cwd, status, exitCode, durationMs |
fileChange |
Thay đổi file | id, changes [{path, kind, diff}], status |
mcpToolCall |
Gọi MCP tool | id, server, tool, status, arguments, result/error |
collabToolCall |
Gọi collab tool | id, tool, status, senderThreadId... |
webSearch |
Tìm kiếm web | id, query, action |
imageView |
Xem ảnh | id, path |
enteredReviewMode |
Bắt đầu review | id, review |
exitedReviewMode |
Kết thúc review | id, review (kết quả) |
contextCompaction |
Nén context | id |
Bạn có thể tắt notification cụ thể khi initialize:
{
"capabilities": {
"optOutNotificationMethods": [
"item/agentMessage/delta",
"codex/event/session_configured"
]
}
}- Exact-match only (không hỗ trợ wildcard)
- Method name không tồn tại → bị ignore
Tuỳ vào cấu hình sandbox, Codex có thể yêu cầu approval trước khi thực thi command hoặc thay đổi file.
sequenceDiagram
participant S as Server
participant C as Client
S-->>C: item/started (commandExecution, status: pending)
S->>C: item/commandExecution/requestApproval
Note right of C: Client hiển thị UI cho user
C->>S: Response (accept / decline / cancel)
S-->>C: item/completed (status: completed / declined)
Decisions hỗ trợ:
| Decision | Ý nghĩa |
|---|---|
accept |
Chấp nhận lần này |
acceptForSession |
Chấp nhận cho cả session |
decline |
Từ chối |
cancel |
Huỷ turn |
acceptWithExecpolicyAmendment |
Chấp nhận + thêm policy rule |
Với acceptWithExecpolicyAmendment:
{
"acceptWithExecpolicyAmendment": {
"execpolicy_amendment": ["npm", "test"]
}
}Tương tự nhưng dùng item/fileChange/requestApproval. Decisions: accept, acceptForSession, decline, cancel.
App connector tool calls có side effects → server gửi tool/requestUserInput với options Accept/Decline/Cancel.
| Policy | Mô tả | Network |
|---|---|---|
readOnly |
Chỉ đọc | Không |
workspaceWrite |
Ghi trong writableRoots | Boolean (true/false) |
externalSandbox |
Sandbox do host quản lý | restricted / enabled |
dangerFullAccess |
Full quyền (nguy hiểm) | Full |
// Chỉ đọc, full access
{ "type": "readOnly", "access": { "type": "fullAccess" } }
// Ghi trong project, đọc thêm shared folder
{
"type": "workspaceWrite",
"writableRoots": ["/Users/me/project"],
"readOnlyAccess": {
"type": "restricted",
"includePlatformDefaults": true,
"readableRoots": ["/Users/me/shared-read-only"]
},
"networkAccess": false
}
// External sandbox
{
"type": "externalSandbox",
"networkAccess": "restricted"
}Codex App Server hỗ trợ 3 chế độ xác thực:
graph LR
subgraph auth ["🔐 Authentication Modes"]
A1["API Key<br/>(apikey)"]
A2["ChatGPT Managed<br/>(chatgpt)"]
A3["ChatGPT External<br/>(chatgptAuthTokens)"]
end
A1 --> USE1["Đơn giản<br/>Dùng API key trực tiếp"]
A2 --> USE2["OAuth Flow<br/>Codex quản lý tokens"]
A3 --> USE3["Host App cung cấp<br/>idToken + accessToken"]
style A1 fill:#0984e3,color:#fff,stroke:#74b9ff
style A2 fill:#00b894,color:#fff,stroke:#55efc4
style A3 fill:#e17055,color:#fff,stroke:#fab1a0
| Method | Loại | Mô tả |
|---|---|---|
account/read |
Request | Kiểm tra trạng thái auth |
account/login/start |
Request | Bắt đầu login |
account/login/completed |
Notification | Login hoàn thành |
account/login/cancel |
Request | Huỷ ChatGPT login |
account/logout |
Request | Đăng xuất |
account/updated |
Notification | Auth mode thay đổi |
account/chatgptAuthTokens/refresh |
Server Request | Yêu cầu refresh tokens |
account/rateLimits/read |
Request | Xem rate limits |
account/rateLimits/updated |
Notification | Rate limits thay đổi |
// 1. Send
{ "method": "account/login/start", "id": 2, "params": { "type": "apiKey", "apiKey": "sk-..." } }
// 2. Response
{ "id": 2, "result": { "type": "apiKey" } }
// 3. Notifications
{ "method": "account/login/completed", "params": { "loginId": null, "success": true, "error": null } }
{ "method": "account/updated", "params": { "authMode": "apikey" } }// 1. Start
{ "method": "account/login/start", "id": 3, "params": { "type": "chatgpt" } }
// 2. Response — nhận authUrl
{
"id": 3,
"result": {
"type": "chatgpt",
"loginId": "<uuid>",
"authUrl": "https://chatgpt.com/...&redirect_uri=http%3A%2F%2Flocalhost%3A<port>%2Fauth%2Fcallback"
}
}
// 3. Mở authUrl trong browser → chờ callback
// 4. Notifications
{ "method": "account/login/completed", "params": { "loginId": "<uuid>", "success": true } }
{ "method": "account/updated", "params": { "authMode": "chatgpt" } }// Host app cung cấp tokens trực tiếp
{
"method": "account/login/start", "id": 7,
"params": {
"type": "chatgptAuthTokens",
"idToken": "<jwt>",
"accessToken": "<jwt>"
}
}Khi server gặp 401 → gửi account/chatgptAuthTokens/refresh yêu cầu host app cung cấp tokens mới (timeout ~10s).
{ "method": "account/rateLimits/read", "id": 6 }Response chứa:
rateLimits— backward-compatible single-bucket viewrateLimitsByLimitId— multi-bucket view (ví dụ:codex,codex_other)- Mỗi bucket có:
usedPercent,windowDurationMins,resetsAt(Unix timestamp)
review/start chạy Codex reviewer trên một thread.
| Target | Mô tả |
|---|---|
uncommittedChanges |
Review thay đổi chưa commit |
baseBranch |
Diff với một branch |
commit |
Review một commit cụ thể |
custom |
Free-form instructions |
| Mode | Mô tả |
|---|---|
inline (default) |
Review trên thread hiện tại |
detached |
Fork thread mới cho review |
{
"method": "review/start", "id": 40,
"params": {
"threadId": "thr_123",
"delivery": "inline",
"target": { "type": "commit", "sha": "1234567deadbeef", "title": "Polish tui colors" }
}
}Server stream: item/started (enteredReviewMode) → ... → item/completed (exitedReviewMode với review text).
command/exec chạy command trực tiếp dưới sandbox, không cần tạo thread/turn.
{
"method": "command/exec", "id": 50,
"params": {
"command": ["ls", "-la"],
"cwd": "/Users/me/project",
"sandboxPolicy": { "type": "workspaceWrite" },
"timeoutMs": 10000
}
}Response:
{ "id": 50, "result": { "exitCode": 0, "stdout": "...", "stderr": "" } }Lưu ý:
commandarray rỗng → bị rejectsandboxPolicychấp nhận cùng shape vớiturn/starttimeoutMskhông set → dùng server default
Thêm $<skill-name> vào text input + skill item:
{
"method": "turn/start", "id": 101,
"params": {
"threadId": "thread-1",
"input": [
{ "type": "text", "text": "$skill-creator Add a new skill for triaging flaky CI." },
{ "type": "skill", "name": "skill-creator", "path": "/Users/me/.codex/skills/skill-creator/SKILL.md" }
]
}
}Nếu bỏ
skillitem, model vẫn parse$<skill-name>nhưng sẽ chậm hơn.
{
"method": "skills/list", "id": 25,
"params": {
"cwds": ["/Users/me/project"],
"forceReload": true,
"perCwdExtraUserRoots": [
{ "cwd": "/Users/me/project", "extraUserRoots": ["/Users/me/shared-skills"] }
]
}
}{
"method": "skills/config/write", "id": 26,
"params": { "path": "/Users/me/.codex/skills/skill-creator/SKILL.md", "enabled": false }
}Thêm $<app-slug> vào text + mention item:
{
"method": "turn/start", "id": 51,
"params": {
"threadId": "thread-1",
"input": [
{ "type": "text", "text": "$demo-app Pull the latest updates from the team." },
{ "type": "mention", "name": "Demo App", "path": "app://demo-app" }
]
}
}{ "method": "app/list", "id": 50, "params": { "cursor": null, "limit": 50, "forceRefetch": false } }Mỗi app entry có:
isAccessible— user có quyền truy cập khôngisEnabled— đã bật trong config.toml chưa
{ "method": "model/list", "id": 6, "params": { "limit": 20, "includeHidden": false } }Mỗi model entry chứa:
| Field | Mô tả |
|---|---|
reasoningEffort |
Các mức effort hỗ trợ |
defaultReasoningEffort |
Effort mặc định |
upgrade |
Model ID khuyến nghị upgrade |
hidden |
Ẩn khỏi picker mặc định |
inputModalities |
Input types hỗ trợ (text, image) |
supportsPersonality |
Có hỗ trợ /personality không |
isDefault |
Có phải model mặc định không |
{ "method": "experimentalFeature/list", "id": 7, "params": { "limit": 20 } }stage values: beta, underDevelopment, stable, deprecated, removed.
| Method | Mô tả |
|---|---|
config/read |
Đọc config hiện hành (đã resolve layering) |
config/value/write |
Ghi một key/value vào config.toml |
config/batchWrite |
Ghi nhiều key/value atomically |
configRequirements/read |
Đọc requirements (allow-lists, residency...) |
config/mcpServer/reload |
Reload MCP server config từ disk |
Khi turn fail, server gửi error event rồi kết thúc turn với status: "failed":
{
"turn": {
"status": "failed",
"error": {
"message": "Context window exceeded",
"codexErrorInfo": { "type": "ContextWindowExceeded", "httpStatusCode": 400 },
"additionalDetails": "..."
}
}
}| Error Type | Mô tả | Cách xử lý |
|---|---|---|
ContextWindowExceeded |
Vượt context window | Dùng thread/compact/start hoặc thread/rollback |
UsageLimitExceeded |
Vượt quota | Chờ reset hoặc upgrade plan |
HttpConnectionFailed |
Upstream 4xx/5xx | Retry với backoff |
ResponseStreamConnectionFailed |
Mất kết nối stream | Reconnect + resume thread |
ResponseStreamDisconnected |
Stream bị ngắt | Reconnect + resume thread |
ResponseTooManyFailedAttempts |
Quá nhiều retry | Kiểm tra config/auth |
BadRequest |
Request không hợp lệ | Kiểm tra params |
Unauthorized |
Chưa auth / token hết hạn | Re-authenticate |
SandboxError |
Lỗi sandbox | Kiểm tra sandbox policy |
InternalServerError |
Lỗi server | Retry hoặc report |
| Error Code | Message | Xử lý |
|---|---|---|
-32001 |
Server overloaded; retry later. |
Retry với exponential backoff + jitter |
| Method | Mô tả | Experimental |
|---|---|---|
initialize |
Khởi tạo kết nối | ❌ |
thread/start |
Tạo thread mới | ❌ |
thread/resume |
Resume thread | ❌ |
thread/fork |
Fork thread | ❌ |
thread/read |
Đọc thread (không resume) | ❌ |
thread/list |
List threads (pagination) | ❌ |
thread/loaded/list |
List threads trong memory | ❌ |
thread/archive |
Archive thread | ❌ |
thread/unarchive |
Unarchive thread | ❌ |
thread/compact/start |
Trigger compaction | ❌ |
thread/rollback |
Rollback N turns | ❌ |
turn/start |
Bắt đầu turn | ❌ |
turn/steer |
Bổ sung input vào turn đang chạy | ❌ |
turn/interrupt |
Huỷ turn | ❌ |
review/start |
Bắt đầu code review | ❌ |
command/exec |
Chạy command ngoài thread | ❌ |
model/list |
List models | ❌ |
experimentalFeature/list |
List feature flags | ✅ |
collaborationMode/list |
List collaboration modes | ✅ |
skills/list |
List skills | ❌ |
skills/config/write |
Enable/disable skill | ❌ |
app/list |
List apps (connectors) | ❌ |
mcpServer/oauth/login |
Start MCP OAuth login | ❌ |
tool/requestUserInput |
Prompt user (1-3 questions) | ✅ |
config/mcpServer/reload |
Reload MCP config | ❌ |
mcpServerStatus/list |
List MCP server status | ❌ |
feedback/upload |
Gửi feedback report | ❌ |
config/read |
Đọc config | ❌ |
config/value/write |
Ghi config value | ❌ |
config/batchWrite |
Ghi config batch | ❌ |
configRequirements/read |
Đọc config requirements | ❌ |
account/read |
Kiểm tra auth state | ❌ |
account/login/start |
Bắt đầu login | ❌ |
account/login/cancel |
Huỷ ChatGPT login | ❌ |
account/logout |
Đăng xuất | ❌ |
account/rateLimits/read |
Xem rate limits | ❌ |
| Method | Mô tả |
|---|---|
thread/started |
Thread đã tạo/resume |
turn/started |
Turn bắt đầu |
turn/completed |
Turn hoàn thành |
turn/diff/updated |
Diff tổng hợp cập nhật |
turn/plan/updated |
Plan cập nhật |
thread/tokenUsage/updated |
Token usage thay đổi |
item/started |
Item bắt đầu |
item/completed |
Item hoàn thành |
item/agentMessage/delta |
Agent message text chunk |
item/plan/delta |
Plan text chunk |
item/reasoning/summaryTextDelta |
Reasoning summary chunk |
item/reasoning/summaryPartAdded |
Reasoning section boundary |
item/reasoning/textDelta |
Raw reasoning chunk |
item/commandExecution/outputDelta |
Command stdout/stderr chunk |
item/fileChange/outputDelta |
File change tool response |
item/commandExecution/requestApproval |
Yêu cầu approve command |
item/fileChange/requestApproval |
Yêu cầu approve file change |
account/login/completed |
Login hoàn thành |
account/updated |
Auth mode thay đổi |
account/rateLimits/updated |
Rate limits thay đổi |
account/chatgptAuthTokens/refresh |
Yêu cầu refresh tokens |
mcpServer/oauthLogin/completed |
MCP OAuth hoàn thành |
app/list/updated |
App list thay đổi |
fuzzyFileSearch/sessionUpdated |
File search results (experimental) |
fuzzyFileSearch/sessionCompleted |
File search done (experimental) |
- Gửi
initialize+initializedtrước mọi thứ - Xử lý
Not initializederror gracefully - Implement approval UI cho commands + file changes
- Buffer
item/agentMessage/deltađể render streaming text - Xử lý
turn/completedvới cả 3 status:completed,interrupted,failed - Implement reconnection logic (đặc biệt cho WebSocket)
- Store
thread.idđể resume sessions - Set
clientInfo.nameđúng cách cho compliance logs
- Dùng
optOutNotificationMethodsđể tắt notifications không cần (giảm bandwidth) - Dùng
thread/compact/startkhi context window gần đầy - Dùng
thread/rollbackthay vì tạo thread mới khi chỉ cần undo vài turns - Cache
model/listvàapp/listresponses, chỉ refresh khi cần
- Luôn dùng
sandboxPolicyphù hợp — tránhdangerFullAccesstrong production - Dùng
readOnlyAccess.restricted+readableRootsđể giới hạn đọc - Validate
writableRoots— chỉ cho phép ghi vào project directory - Dùng
approvalPolicy: "unlessTrusted"cho production clients - Khi host app quản lý tokens (
chatgptAuthTokens) — implementaccount/chatgptAuthTokens/refreshhandler
Client Server
|--- connect ws://... -------->|
|--- initialize -------------->|
|<-- result -------------------|
|--- initialized -------------->|
|--- thread/start ------------->|
|<-- result -------------------|
|--- turn/start --------------->|
|<-- item/* stream ------------|
| (connection drops) |
|--- reconnect ws://... ------>|
|--- initialize -------------->|
|--- thread/resume ----------->| ← Resume, không start mới
|--- turn/start --------------->|
|<-- item/* stream ------------|
| Tình huống | Method |
|---|---|
| Tạo conversation mới | thread/start |
| Tiếp tục conversation cũ | thread/resume |
| Branch từ conversation | thread/fork |
| Xem lịch sử (không tiếp tục) | thread/read |
| Render danh sách conversations | thread/list |
| Gửi input cho agent | turn/start |
| Bổ sung context giữa chừng | turn/steer |
| Dừng agent | turn/interrupt |
| Review code changes | review/start |
| Chạy lệnh nhanh | command/exec |
| Context window đầy | thread/compact/start |
| Undo turns | thread/rollback |
Nguyên nhân: Gửi request trước khi hoàn thành handshake.
Giải pháp: Đảm bảo gửi initialize request → nhận response → gửi initialized notification → rồi mới gửi request khác.
Nguyên nhân: Gọi initialize lần 2 trên cùng connection.
Giải pháp: Chỉ gọi initialize một lần per connection. Nếu cần reset, tạo connection mới.
Nguyên nhân: Gọi experimental method/field mà chưa opt-in.
Giải pháp: Thêm "capabilities": { "experimentalApi": true } trong initialize.
Nguyên nhân: Request queue đầy.
Giải pháp: Implement retry với exponential backoff + jitter:
const retry = async (fn: () => Promise<any>, maxRetries = 5) => {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (e: any) {
if (e.code === -32001) {
const delay = Math.min(1000 * Math.pow(2, i), 30000);
const jitter = Math.random() * delay * 0.3;
await new Promise(r => setTimeout(r, delay + jitter));
continue;
}
throw e;
}
}
};Nguyên nhân: Hội thoại quá dài.
Giải pháp:
thread/compact/start— nén lịch sửthread/rollback— xoá turns không cần- Tạo thread mới nếu compact không đủ
Nguyên nhân: MCP server đánh dấu required nhưng không khởi tạo được.
Giải pháp: Kiểm tra MCP server config, đảm bảo server đang chạy, rồi thử config/mcpServer/reload.
Hành vi: Codex emit warning và apply one-time model-switch instruction trên turn tiếp theo. Không phải bug, là design.
| Tài liệu | Link | Mô tả |
|---|---|---|
| Codex Overview | developers.openai.com/codex | Tổng quan Codex |
| Codex SDK | developers.openai.com/codex/sdk | SDK cho CI/CD automation |
| MCP Server | developers.openai.com/codex/guides/agents-sdk | Codex as MCP Server |
| Non-interactive Mode | developers.openai.com/codex/noninteractive | Chạy Codex không tương tác |
| GitHub Action | developers.openai.com/codex/github-action | CI/CD integration |
| Config Reference | developers.openai.com/codex/config-reference | Cấu hình chi tiết |
| AGENTS.md | developers.openai.com/codex/guides/agents-md | Hướng dẫn AGENTS.md |
| Skills | developers.openai.com/codex/skills | Hệ thống Skills |
| Open Source | developers.openai.com/codex/open-source | Các component open-source |