diff --git a/README.md b/README.md
index ab69849..b669a3d 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,8 @@ This is a tutorial project of [Pocket Flow](https://github.com/The-Pocket/Pocket
- [Click](https://the-pocket.github.io/Tutorial-Codebase-Knowledge/Click) - Turn Python functions into slick command-line tools with just a decorator!
+- [Codex](https://the-pocket.github.io/Tutorial-Codebase-Knowledge/Codex) - Turn plain English into working code with this AI terminal wizard!
+
- [Crawl4AI](https://the-pocket.github.io/Tutorial-Codebase-Knowledge/Crawl4AI) - Train your AI to extract exactly what matters from any website!
- [CrewAI](https://the-pocket.github.io/Tutorial-Codebase-Knowledge/CrewAI) - Assemble a dream team of AI specialists to tackle impossible problems!
diff --git a/docs/Codex/01_terminal_ui__ink_components_.md b/docs/Codex/01_terminal_ui__ink_components_.md
new file mode 100644
index 0000000..7beadde
--- /dev/null
+++ b/docs/Codex/01_terminal_ui__ink_components_.md
@@ -0,0 +1,340 @@
+---
+layout: default
+title: "Terminal UI (Ink Components)"
+parent: "Codex"
+nav_order: 1
+---
+
+# Chapter 1: Terminal UI (Ink Components)
+
+Welcome to the Codex tutorial! We're excited to have you explore how Codex works under the hood. This first chapter dives into how Codex creates its chat interface right inside your terminal window.
+
+## What's the Big Idea?
+
+Imagine you want `Codex` to write a simple script. You type something like `codex "write a python script that prints hello world"` into your terminal. How does Codex show you the conversation – your request, its response, maybe questions it asks, or commands it suggests running – all without opening a separate window? And how do you type your next message?
+
+That's where the **Terminal UI** comes in. It's the system responsible for drawing the entire chat interface you see and interact with directly in your command line.
+
+Think of it like the dashboard and controls of a car:
+
+* **Dashboard:** Displays information (like the chat history, AI messages, loading indicators).
+* **Controls (Steering Wheel, Pedals):** Let you interact (like the input field where you type messages, or menus to approve commands).
+
+Just like the car's dashboard lets you see what the engine is doing and control it, the Terminal UI lets you see what the core `Codex` logic (the [Agent Loop](03_agent_loop.md)) is doing and provide input to it.
+
+## Key Concepts: Ink & React
+
+How does Codex build this terminal interface? It uses two main technologies:
+
+1. **Ink:** This is a fantastic library that lets developers build command-line interfaces using **React**. If you know React for web development, Ink feels very similar, but instead of rendering buttons and divs in a browser, it renders text, boxes, and lists in your terminal.
+
+2. **React Components:** The UI is broken down into reusable pieces called React components. We have components for:
+ * Displaying individual messages (`TerminalChatResponseItem`).
+ * Showing the whole conversation history (`MessageHistory`).
+ * The text box where you type your input (`TerminalChatInput` / `TerminalChatNewInput`).
+ * Prompts asking you to approve commands (`TerminalChatCommandReview`).
+ * Spinners to show when Codex is thinking.
+
+These components work together, managed by React, to create the dynamic interface you see.
+
+## How You See It: Rendering the Chat
+
+When you run `Codex`, the main application component (`App` in `app.tsx`) kicks things off. It might first check if you're in a safe directory (like a Git repository) and ask for confirmation if not.
+
+```tsx
+// File: codex-cli/src/app.tsx (Simplified)
+
+// ... imports ...
+import TerminalChat from "./components/chat/terminal-chat";
+import { ConfirmInput } from "@inkjs/ui";
+import { Box, Text, useApp } from "ink";
+import React, { useState } from "react";
+
+export default function App({ /* ...props... */ }): JSX.Element {
+ const app = useApp();
+ const [accepted, setAccepted] = useState(/* ... */);
+ const inGitRepo = /* ... check if in git ... */;
+
+ // If not in a git repo and not yet accepted, show a warning
+ if (!inGitRepo && !accepted) {
+ return (
+
+ Warning! Not in a git repo.
+ setAccepted(true)}
+ onCancel={() => app.exit()}
+ />
+
+ );
+ }
+
+ // Otherwise, render the main chat interface
+ return ;
+}
+```
+
+This snippet shows how the `App` component uses Ink's ``, ``, and even interactive components like ``. If the safety check passes, it renders the core `` component.
+
+The `` component (`terminal-chat.tsx`) is the main hub for the chat UI. It manages the state, like the list of messages (`items`), whether the AI is currently working (`loading`), and any command confirmations needed (`confirmationPrompt`).
+
+```tsx
+// File: codex-cli/src/components/chat/terminal-chat.tsx (Simplified)
+
+// ... imports ...
+import TerminalMessageHistory from "./terminal-message-history";
+import TerminalChatInput from "./terminal-chat-input"; // Or TerminalChatNewInput
+import { Box } from "ink";
+import React, { useState } from "react";
+
+export default function TerminalChat({ /* ...props... */ }): React.ReactElement {
+ const [items, setItems] = useState>([]); // Holds all messages
+ const [loading, setLoading] = useState(false); // Is the AI busy?
+ const [confirmationPrompt, setConfirmationPrompt] = useState(null); // Command to review?
+ // ... other state and logic ...
+
+ return (
+
+ {/* Display the conversation history */}
+
+
+ {/* Display the input box or the command review prompt */}
+ { /* Send to Agent Loop */ }}
+ submitConfirmation={(/*...decision...*/) => { /* Send to Agent Loop */ }}
+ /* ...other props... */
+ />
+
+ );
+}
+```
+
+* `` takes the list of `items` (messages) and renders them.
+* `` (or its multiline sibling ``) displays the input box when `loading` is false and there's no `confirmationPrompt`. If there *is* a `confirmationPrompt`, it shows the command review UI instead.
+
+### Showing Messages
+
+How does `` actually display the messages? It uses a special Ink component called `` for efficiency and maps each message `item` to a ``.
+
+```tsx
+// File: codex-cli/src/components/chat/terminal-message-history.tsx (Simplified)
+
+// ... imports ...
+import TerminalChatResponseItem from "./terminal-chat-response-item";
+import { Box, Static } from "ink";
+import React from "react";
+
+const MessageHistory: React.FC = ({ batch, /* ... */ }) => {
+ // Extract the actual message objects
+ const messages = batch.map(({ item }) => item!);
+
+ return (
+
+ {/* renders past items efficiently */}
+
+ {(message, index) => (
+ // Render each message using TerminalChatResponseItem
+
+
+
+ )}
+
+
+ );
+};
+
+export default React.memo(MessageHistory);
+```
+
+`` tells Ink that these items won't change often, allowing Ink to optimize rendering. Each message is passed to ``.
+
+Inside `TerminalChatResponseItem` (`terminal-chat-response-item.tsx`), we figure out what *kind* of message it is (user message, AI response, command output, etc.) and render it accordingly using Ink's basic `` and `` components, sometimes with helpers like `` for formatting.
+
+```tsx
+// File: codex-cli/src/components/chat/terminal-chat-response-item.tsx (Simplified)
+
+// ... imports ...
+import { Box, Text } from "ink";
+import React from "react";
+// ... other components like Markdown ...
+
+export default function TerminalChatResponseItem({ item }: { item: ResponseItem }): React.ReactElement {
+ switch (item.type) {
+ case "message": // User or AI text message
+ return (
+
+
+ {item.role === "assistant" ? "codex" : item.role}
+
+ {/* Render message content, potentially using Markdown */}
+ {/* ... content ... */}
+
+ );
+ case "function_call": // AI wants to run a command
+ return (
+
+ command
+ $ {/* Formatted command */}
+
+ );
+ // ... other cases like function_call_output ...
+ default:
+ return Unknown message type ;
+ }
+}
+```
+
+### Getting Your Input
+
+The `` (or ``) component uses specialized input components (like `` from `ink-text-input` or our custom ``) to capture your keystrokes. When you press Enter, it calls the `onSubmit` or `submitInput` function provided by ``.
+
+```tsx
+// File: codex-cli/src/components/chat/terminal-chat-new-input.tsx (Simplified)
+
+// ... imports ...
+import MultilineTextEditor from "./multiline-editor"; // Custom multiline input
+import { Box, Text, useInput } from "ink";
+import React, { useState } from "react";
+
+export default function TerminalChatInput({ submitInput, active, /* ... */ }): React.ReactElement {
+ const [input, setInput] = useState(""); // Current text in the editor
+ const editorRef = React.useRef(/* ... */); // Handle to editor
+
+ // useInput hook from Ink handles key presses (like Up/Down for history)
+ useInput((_input, _key) => {
+ // Handle history navigation (Up/Down arrows)
+ // ... logic using editorRef.current.getRow() ...
+ }, { isActive: active });
+
+ return (
+
+
+ {/* The actual input field */}
+ setInput(txt)}
+ initialText={input}
+ focus={active} // Only active when overlay isn't shown
+ onSubmit={(text) => {
+ // When Enter is pressed (and not escaped)
+ submitInput(/* ...create input item from text... */);
+ setInput(""); // Clear the input field
+ }}
+ />
+
+ {/* Help text */}
+ ctrl+c to exit | enter to send
+
+ );
+}
+```
+
+This component manages the text you type and uses Ink's `useInput` hook to handle special keys like arrow keys for command history. The details of text editing are handled in the next chapter: [Input Handling (TextBuffer/Editor)](02_input_handling__textbuffer_editor_.md).
+
+### Reviewing Commands
+
+If the [Agent Loop](03_agent_loop.md) decides it needs to run a command and requires your approval, `` will receive a `confirmationPrompt`. This prompt (which is itself a React element, often ``) is passed down to ``, which then renders `` instead of the regular input box.
+
+```tsx
+// File: codex-cli/src/components/chat/terminal-chat-command-review.tsx (Simplified)
+
+// ... imports ...
+// @ts-expect-error - Using a vendor component for selection
+import { Select } from "../vendor/ink-select/select";
+import TextInput from "../vendor/ink-text-input"; // For editing feedback
+import { Box, Text, useInput } from "ink";
+import React from "react";
+
+export function TerminalChatCommandReview({
+ confirmationPrompt, // The command display element
+ onReviewCommand, // Function to call with the decision
+}: { /* ... */ }): React.ReactElement {
+ const [mode, setMode] = React.useState<"select" | "input">("select"); // Select Yes/No or type feedback
+
+ // Options for the selection list
+ const approvalOptions = [
+ { label: "Yes (y)", value: ReviewDecision.YES },
+ // ... other options like Always, Edit, No ...
+ ];
+
+ useInput((input, key) => { /* Handle shortcuts like 'y', 'n', 'e', Esc */ });
+
+ return (
+
+ {/* Display the command that needs review */}
+ {confirmationPrompt}
+
+ {mode === "select" ? (
+ <>
+ Allow command?
+ { /* ... call onReviewCommand or setMode('input') ... */ }}
+ />
+ >
+ ) : (
+ /* UI for typing feedback (TextInput) */
+ // ...
+ )}
+
+ );
+}
+```
+
+This component shows the command passed in (`confirmationPrompt`), presents options using Ink's `` component (or a `` if you choose to edit/give feedback), listens for your choice (via keyboard shortcuts or the selection list), and finally calls `onReviewCommand` with your decision.
+
+## Under the Hood: How It All Connects
+
+Let's trace the flow from starting Codex to seeing an AI response:
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant Terminal
+ participant CodexCLI
+ participant InkReactApp as Ink/React UI
+ participant AgentLoop as Agent Loop
+
+ User->>Terminal: Runs `codex "prompt"`
+ Terminal->>CodexCLI: Starts the process
+ CodexCLI->>InkReactApp: Renders initial UI (`App` -> `TerminalChat`)
+ InkReactApp->>Terminal: Displays UI (header, empty chat, input box)
+ User->>Terminal: Types message, presses Enter
+ Terminal->>InkReactApp: Captures input (`TerminalChatInput`)
+ InkReactApp->>AgentLoop: Sends user input via `submitInput` prop (in `TerminalChat`)
+ Note over AgentLoop: Processes input, calls LLM...
+ AgentLoop->>InkReactApp: Sends back AI response via `onItem` prop (in `TerminalChat`)
+ InkReactApp->>InkReactApp: Updates state (`items`), triggers re-render
+ InkReactApp->>Terminal: Re-renders UI with new message (`MessageHistory`)
+```
+
+1. You run `codex`.
+2. The CLI process starts.
+3. The React application (`App` -> `TerminalChat`) renders the initial UI using Ink components. Ink translates these components into terminal commands to draw the interface.
+4. You type your message into the `` component.
+5. When you press Enter, the input component's `onSubmit` handler is called.
+6. `` receives this, packages it, and calls the `run` method on the [Agent Loop](03_agent_loop.md).
+7. The Agent Loop processes the input (often calling an LLM).
+8. When the Agent Loop has something to display (like the AI's text response), it calls the `onItem` callback function provided by ``.
+9. `` receives the new message item and updates its `items` state using `setItems`.
+10. React detects the state change and tells Ink to re-render the necessary components (like adding the new message to ``).
+11. Ink updates the terminal display.
+
+The process for handling command confirmations is similar, involving the `getCommandConfirmation` and `submitConfirmation` callbacks between `` and the Agent Loop, rendering `` in the UI when needed.
+
+## Conclusion
+
+You've now seen how Codex uses the power of React and the Ink library to build a fully interactive chat interface directly within your terminal. This "Terminal UI" layer acts as the visual front-end, displaying messages, capturing your input, and presenting choices like command approvals, all while coordinating with the core [Agent Loop](03_agent_loop.md) behind the scenes.
+
+But how exactly does that input box capture your keystrokes, handle multi-line editing, and manage command history? We'll explore that in the next chapter.
+
+Next up: [Input Handling (TextBuffer/Editor)](02_input_handling__textbuffer_editor_.md)
+
+---
+
+Generated by [AI Codebase Knowledge Builder](https://github.com/The-Pocket/Tutorial-Codebase-Knowledge)
\ No newline at end of file
diff --git a/docs/Codex/02_input_handling__textbuffer_editor_.md b/docs/Codex/02_input_handling__textbuffer_editor_.md
new file mode 100644
index 0000000..d73bcbb
--- /dev/null
+++ b/docs/Codex/02_input_handling__textbuffer_editor_.md
@@ -0,0 +1,353 @@
+---
+layout: default
+title: "Input Handling (TextBuffer/Editor)"
+parent: "Codex"
+nav_order: 2
+---
+
+# Chapter 2: Input Handling (TextBuffer/Editor)
+
+In the [previous chapter](01_terminal_ui__ink_components_.md), we saw how Codex uses Ink and React to draw the chat interface in your terminal. We learned about components like `` and `` that show an input box. But how does that input box *actually work*?
+
+## Why a Fancy Input Box?
+
+Imagine you want Codex to write a small Python script. You might type something like this:
+
+```python
+Write a python function that:
+1. Takes a list of numbers.
+2. Returns a new list containing only the even numbers.
+Make sure it handles empty lists gracefully.
+```
+
+Or maybe you're reviewing a command Codex proposed and want to give detailed feedback. A simple, single-line input field like your shell's basic prompt would be really awkward for this! You'd want to:
+
+* Write multiple lines easily.
+* Use arrow keys to move your cursor around to fix typos.
+* Maybe jump back a whole word (`Ctrl+LeftArrow`) or delete a word (`Ctrl+Backspace`).
+* Press `Up` or `Down` arrow to bring back previous messages you sent (history).
+* Perhaps even open the current text in your main code editor (like VS Code or Vim) for complex edits (`Ctrl+X`).
+
+This is where the **Input Handling** system comes in. It's like a mini text editor built right into the Codex chat interface, designed to make typing potentially complex prompts and messages much easier than a standard terminal input line.
+
+## Key Idea: The `TextBuffer`
+
+The heart of this system is a class called `TextBuffer` (found in `text-buffer.ts`). Think of `TextBuffer` like the hidden document model behind a simple text editor (like Notepad or TextEdit):
+
+* **It holds the text:** It stores all the lines of text you've typed into the input box in an internal list (an array of strings called `lines`).
+* **It knows where the cursor is:** It keeps track of the cursor's position (which `row` and `column` it's on).
+* **It handles edits:** When you press keys like letters, numbers, Backspace, Delete, or Enter, the `TextBuffer` modifies the text and updates the cursor position accordingly.
+* **It manages scrolling:** If your text gets longer than the input box can display, the `TextBuffer` figures out which part of the text should be visible.
+
+The `MultilineTextEditor` React component we saw in Chapter 1 uses an instance of this `TextBuffer` internally to manage the state of the text being edited.
+
+## How You Use It (Indirectly)
+
+You don't directly interact with `TextBuffer` yourself. You interact with the `` component displayed by Ink. But understanding `TextBuffer` helps you see *how* the editor works.
+
+Let's look at a simplified view of how the `` component uses ``:
+
+```tsx
+// File: codex-cli/src/components/chat/terminal-chat-new-input.tsx (Simplified)
+import React, { useState, useCallback } from "react";
+import { Box, Text, useInput } from "ink";
+import MultilineTextEditor from "./multiline-editor"; // Our editor component
+// ... other imports
+
+export default function TerminalChatInput({ submitInput, active, /* ... */ }) {
+ const [input, setInput] = useState(""); // Holds the current text in the editor state
+ const [history, setHistory] = useState([]); // Holds past submitted messages
+ const [historyIndex, setHistoryIndex] = useState(null);
+ // Used to force re-render editor when history changes text
+ const [editorKey, setEditorKey] = useState(0);
+ const editorRef = React.useRef(/* ... */); // Handle to the editor
+
+ // --- History Handling (Simplified) ---
+ useInput((_input, key) => {
+ // Check if Up/Down arrow pressed AND cursor is at top/bottom line
+ const isAtTop = editorRef.current?.isCursorAtFirstRow();
+ const isAtBottom = editorRef.current?.isCursorAtLastRow();
+
+ if (key.upArrow && isAtTop && history.length > 0) {
+ // Logic to go back in history
+ const newIndex = historyIndex === null ? history.length - 1 : Math.max(0, historyIndex - 1);
+ setHistoryIndex(newIndex);
+ setInput(history[newIndex] ?? ""); // Set the text to the historical item
+ setEditorKey(k => k + 1); // Force editor to re-mount with new text
+ // ... save draft if needed ...
+ } else if (key.downArrow && isAtBottom && historyIndex !== null) {
+ // Logic to go forward in history or restore draft
+ // ... similar logic using setInput, setHistoryIndex, setEditorKey ...
+ }
+ // Note: If not handling history, the key press falls through to MultilineTextEditor
+ }, { isActive: active });
+
+
+ // --- Submission Handling ---
+ const onSubmit = useCallback((textFromEditor: string) => {
+ const trimmedText = textFromEditor.trim();
+ if (!trimmedText) return; // Ignore empty submissions
+
+ // Add to history
+ setHistory(prev => [...prev, textFromEditor]);
+ setHistoryIndex(null); // Reset history navigation
+
+ // Send the input to the Agent Loop!
+ submitInput(/* ... create input item from trimmedText ... */);
+
+ // Clear the input for the next message
+ setInput("");
+ setEditorKey(k => k + 1); // Force editor reset
+
+ }, [submitInput, setHistory /* ... */]);
+
+ return (
+
+ {/* The actual editor component */}
+ setInput(text)} // Update React state when text changes internally
+ onSubmit={onSubmit} // Tell editor what to do on Enter
+ height={8} // Example height
+ />
+ ctrl+c exit | enter send | ↑↓ history | ctrl+x editor
+
+ );
+}
+```
+
+* **`initialText={input}`:** The `` starts with the text held in the `input` state variable. This is how history navigation works – we change `input` and force a re-render.
+* **`onChange={(text) => setInput(text)}`:** Whenever the text *inside* the `MultilineTextEditor` (managed by its internal `TextBuffer`) changes, it calls this function. We update the `input` state variable in the parent component (`TerminalChatNewInput`) to keep track, though often the editor manages its own state primarily.
+* **`onSubmit={onSubmit}`:** When you press Enter (in a way that signifies submission, not just adding a newline), the `MultilineTextEditor` calls this `onSubmit` function, passing the final text content. This function then sends the message off to the [Agent Loop](03_agent_loop.md) and clears the input.
+* **History (`useInput`):** The parent component (`TerminalChatNewInput`) uses Ink's `useInput` hook to *intercept* the Up/Down arrow keys *before* they even reach the `MultilineTextEditor`. It checks if the cursor (using `editorRef.current?.isCursorAtFirstRow()`) is at the very top/bottom edge of the text. If so, it handles history navigation by changing the `input` state and forcing the editor to update using `setEditorKey`. If the cursor isn't at the edge, it lets the arrow key "fall through" to the `MultilineTextEditor`, which then just moves the cursor normally within the text via its internal `TextBuffer`.
+
+## Under the Hood: Keystroke to Display
+
+Let's trace what happens when you type a character, say 'h', into the input box:
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant Terminal
+ participant InkUI as Ink/React (MultilineTextEditor)
+ participant TextBuffer
+ participant AgentLoop as Agent Loop (Not involved)
+
+ User->>Terminal: Presses 'h' key
+ Terminal->>InkUI: Terminal sends key event to Ink
+ InkUI->>InkUI: `useInput` hook captures 'h'
+ InkUI->>TextBuffer: Calls `handleInput('h', { ... }, viewport)`
+ TextBuffer->>TextBuffer: Finds current line ("") and cursor (0,0)
+ TextBuffer->>TextBuffer: Calls `insert('h')`
+ TextBuffer->>TextBuffer: Updates `lines` to `["h"]`
+ TextBuffer->>TextBuffer: Updates `cursorCol` to 1
+ TextBuffer->>TextBuffer: Increments internal `version`
+ TextBuffer-->>InkUI: Returns `true` (buffer was modified)
+ InkUI->>InkUI: Triggers a React re-render because internal state changed
+ InkUI->>TextBuffer: Calls `getVisibleLines(viewport)` -> returns `["h"]`
+ InkUI->>TextBuffer: Calls `getCursor()` -> returns `[0, 1]`
+ InkUI->>Terminal: Renders the updated text ("h") with cursor highlight
+```
+
+1. **Keystroke:** You press the 'h' key.
+2. **Capture:** Ink's `useInput` hook within `` receives the key event.
+3. **Delegate:** `` calls the `handleInput` method on its internal `TextBuffer` instance, passing the input character ('h'), key modifier flags (like Shift, Ctrl - none in this case), and the current visible area size (viewport).
+4. **Update State:** `TextBuffer.handleInput` determines it's a simple character insertion. It calls its internal `insert` method.
+5. **`insert` Method:**
+ * Gets the current line (e.g., `""`).
+ * Splits the line at the cursor position (0).
+ * Inserts the character: `""` + `'h'` + `""` -> `"h"`.
+ * Updates the `lines` array: `["h"]`.
+ * Updates the cursor column: `0` -> `1`.
+ * Increments an internal version number to track changes.
+6. **Signal Change:** `handleInput` returns `true` because the buffer was modified.
+7. **Re-render:** The `` component detects the change (either via the return value or its internal state update) and triggers a React re-render.
+8. **Get Display Data:** During the render, `` calls methods on the `TextBuffer` like:
+ * `getVisibleLines()`: Gets the lines of text that should currently be visible based on scrolling.
+ * `getCursor()`: Gets the current row and column of the cursor.
+9. **Draw:** The component uses this information to render the text (`h`) in the terminal. It uses the cursor position to draw the cursor, often by rendering the character *at* the cursor position with an inverted background color (like `chalk.inverse(char)`).
+
+This same loop happens for every key press: Backspace calls `TextBuffer.backspace()`, arrow keys call `TextBuffer.move()`, Enter calls `TextBuffer.newline()` (or triggers `onSubmit`), etc.
+
+## Diving into `TextBuffer` Code (Simplified)
+
+Let's peek inside `text-buffer.ts`:
+
+```typescript
+// File: codex-cli/src/text-buffer.ts (Simplified)
+
+// Helper to check if a character is part of a "word"
+function isWordChar(ch: string | undefined): boolean {
+ // Simplified: returns true if not whitespace or basic punctuation
+ return ch !== undefined && !/[\s,.;!?]/.test(ch);
+}
+
+// Helper to get the length respecting multi-byte characters (like emoji)
+function cpLen(str: string): number { return Array.from(str).length; }
+// Helper to slice respecting multi-byte characters
+function cpSlice(str: string, start: number, end?: number): string {
+ return Array.from(str).slice(start, end).join('');
+}
+
+
+export default class TextBuffer {
+ // --- Core State ---
+ private lines: string[] = [""]; // The text, line by line
+ private cursorRow = 0; // Cursor's current line number
+ private cursorCol = 0; // Cursor's column (character index) on the line
+ // ... scrollRow, scrollCol for viewport management ...
+ private version = 0; // Increments on each change
+
+ constructor(text = "") {
+ this.lines = text.split("\n");
+ if (this.lines.length === 0) this.lines = [""];
+ // Start cursor at the end
+ this.cursorRow = this.lines.length - 1;
+ this.cursorCol = this.lineLen(this.cursorRow);
+ }
+
+ // --- Internal Helpers ---
+ private line(r: number): string { return this.lines[r] ?? ""; }
+ private lineLen(r: number): number { return cpLen(this.line(r)); }
+ private ensureCursorInRange(): void { /* Makes sure row/col are valid */ }
+
+ // --- Public Accessors ---
+ getCursor(): [number, number] { return [this.cursorRow, this.cursorCol]; }
+ getText(): string { return this.lines.join("\n"); }
+ getVisibleLines(/* viewport */): string[] {
+ // ... calculate visible lines based on scrollRow/Col ...
+ return this.lines; // Simplified: return all lines
+ }
+
+ // --- Editing Operations ---
+ insert(ch: string): void {
+ // ... handle potential newlines by calling insertStr ...
+ const line = this.line(this.cursorRow);
+ // Use cpSlice for multi-byte character safety
+ this.lines[this.cursorRow] =
+ cpSlice(line, 0, this.cursorCol) + ch + cpSlice(line, this.cursorCol);
+ this.cursorCol += cpLen(ch); // Use cpLen
+ this.version++;
+ }
+
+ newline(): void {
+ const line = this.line(this.cursorRow);
+ const before = cpSlice(line, 0, this.cursorCol);
+ const after = cpSlice(line, this.cursorCol);
+
+ this.lines[this.cursorRow] = before; // Keep text before cursor on current line
+ this.lines.splice(this.cursorRow + 1, 0, after); // Insert text after cursor as new line
+
+ this.cursorRow++; // Move cursor down
+ this.cursorCol = 0; // Move cursor to start of new line
+ this.version++;
+ }
+
+ backspace(): void {
+ if (this.cursorCol > 0) { // If not at start of line
+ const line = this.line(this.cursorRow);
+ this.lines[this.cursorRow] =
+ cpSlice(line, 0, this.cursorCol - 1) + cpSlice(line, this.cursorCol);
+ this.cursorCol--;
+ this.version++;
+ } else if (this.cursorRow > 0) { // If at start of line (but not first line)
+ // Merge with previous line
+ const prevLine = this.line(this.cursorRow - 1);
+ const currentLine = this.line(this.cursorRow);
+ const newCol = this.lineLen(this.cursorRow - 1); // Cursor goes to end of merged line
+
+ this.lines[this.cursorRow - 1] = prevLine + currentLine; // Combine lines
+ this.lines.splice(this.cursorRow, 1); // Remove the now-empty current line
+
+ this.cursorRow--;
+ this.cursorCol = newCol;
+ this.version++;
+ }
+ // Do nothing if at row 0, col 0
+ }
+
+ move(dir: 'left' | 'right' | 'up' | 'down' | 'wordLeft' | 'wordRight' | 'home' | 'end'): void {
+ switch (dir) {
+ case 'left':
+ if (this.cursorCol > 0) this.cursorCol--;
+ else if (this.cursorRow > 0) { /* Move to end of prev line */ }
+ break;
+ case 'right':
+ if (this.cursorCol < this.lineLen(this.cursorRow)) this.cursorCol++;
+ else if (this.cursorRow < this.lines.length - 1) { /* Move to start of next line */ }
+ break;
+ case 'up':
+ if (this.cursorRow > 0) {
+ this.cursorRow--;
+ // Try to maintain horizontal position (handle preferredCol logic)
+ this.cursorCol = Math.min(this.cursorCol, this.lineLen(this.cursorRow));
+ }
+ break;
+ // ... other cases (down, home, end) ...
+ case 'wordLeft': {
+ // Scan backwards from cursorCol, skip whitespace, then skip word chars
+ // Update this.cursorCol to the start of the word/whitespace run
+ // ... implementation details ...
+ break;
+ }
+ // ... wordRight ...
+ }
+ this.ensureCursorInRange();
+ }
+
+ // --- High-Level Input Handler ---
+ handleInput(input: string | undefined, key: Record, /* viewport */): boolean {
+ const beforeVersion = this.version;
+ // Check key flags (key.leftArrow, key.backspace, key.ctrl, etc.)
+ // and the `input` character itself.
+ if (key.leftArrow && !key.ctrl && !key.meta) this.move('left');
+ else if (key.rightArrow && !key.ctrl && !key.meta) this.move('right');
+ else if (key.upArrow) this.move('up');
+ else if (key.downArrow) this.move('down');
+ else if ((key.ctrl || key.meta) && key.leftArrow) this.move('wordLeft');
+ // ... handle wordRight, home, end ...
+ else if (key.backspace || input === '\x7f' /* DEL char */) this.backspace();
+ // ... handle delete, newline (Enter) ...
+ else if (input && !key.ctrl && !key.meta) {
+ // If it's a printable character (and not a special key combo)
+ this.insert(input);
+ }
+
+ // ... ensure cursor visible based on viewport ...
+ return this.version !== beforeVersion; // Return true if text changed
+ }
+
+ // --- External Editor ---
+ async openInExternalEditor(): Promise {
+ // 1. Get editor from $VISUAL or $EDITOR env var (fallback to vi/notepad)
+ // 2. Write this.getText() to a temporary file
+ // 3. Use Node's `spawnSync` to run the editor command with the temp file path
+ // (This blocks until the editor is closed)
+ // 4. Read the content back from the temp file
+ // 5. Update this.lines, this.cursorRow, this.cursorCol
+ // 6. Clean up the temp file
+ this.version++;
+ }
+}
+```
+
+* The `lines` array holds the actual text content.
+* `cursorRow` and `cursorCol` track the insertion point.
+* Methods like `insert`, `backspace`, `newline`, and `move` directly manipulate `lines`, `cursorRow`, and `cursorCol`. They use helpers like `cpLen` and `cpSlice` to correctly handle characters that might take up more than one byte (like emojis).
+* `handleInput` acts as the main entry point, deciding which specific editing operation to perform based on the key pressed.
+* `openInExternalEditor` handles the `Ctrl+X` magic by saving to a temp file, running your system's default editor, and reloading the content.
+
+## Conclusion
+
+You've now seen how Codex provides a surprisingly powerful text editing experience right within your terminal. It goes far beyond a simple input line by using the `` component, which relies heavily on the internal `TextBuffer` class. This class manages the text content, cursor position, and editing operations like insertion, deletion, multi-line handling, cursor navigation (including word jumps), and even integration with external editors. This allows you to compose complex prompts or provide detailed feedback without leaving the terminal interface.
+
+With the UI drawn and user input handled, what happens next? How does Codex take your input, think about it, and generate a response or decide to run a command? That's the job of the core logic loop.
+
+Next up: [Agent Loop](03_agent_loop.md)
+
+---
+
+Generated by [AI Codebase Knowledge Builder](https://github.com/The-Pocket/Tutorial-Codebase-Knowledge)
\ No newline at end of file
diff --git a/docs/Codex/03_agent_loop.md b/docs/Codex/03_agent_loop.md
new file mode 100644
index 0000000..f4427f7
--- /dev/null
+++ b/docs/Codex/03_agent_loop.md
@@ -0,0 +1,340 @@
+---
+layout: default
+title: "Agent Loop"
+parent: "Codex"
+nav_order: 3
+---
+
+# Chapter 3: Agent Loop
+
+In the [previous chapter](02_input_handling__textbuffer_editor_.md), we saw how Codex captures your commands and messages using a neat multi-line input editor. But once you hit Enter, where does that input *go*? What part of Codex actually understands your request, talks to the AI, and makes things happen?
+
+Meet the **Agent Loop**, the heart and brain of the Codex CLI.
+
+## What's the Big Idea? Like a Helpful Assistant
+
+Imagine you have a very capable personal assistant. You give them a task, like "Find the latest sales report, summarize it, and email it to the team." Your assistant doesn't just magically do it all at once. They follow a process:
+
+1. **Understand the Request:** Listen carefully to what you asked for.
+2. **Gather Information:** Look for the sales report file.
+3. **Perform Actions:** Read the report, write a summary.
+4. **Ask for Confirmation (if needed):** "I've drafted the summary and email. Should I send it now?"
+5. **Complete the Task:** Send the email after getting your 'yes'.
+6. **Report Back:** Let you know the email has been sent.
+
+The **Agent Loop** in Codex acts much like this assistant. It's the central piece of logic that manages the entire conversation and workflow between you and the AI model (like OpenAI's GPT-4).
+
+Let's take our simple example: You type `codex "write a python script that prints hello world and run it"`.
+
+The Agent Loop is responsible for:
+
+1. Taking your input ("write a python script...").
+2. Sending this request to the powerful AI model via the OpenAI API.
+3. Getting the AI's response, which might include:
+ * Text: "Okay, here's the script..."
+ * A request to perform an action (a "function call"): "I need to run this command: `python -c 'print(\"hello world\")'`"
+4. Showing you the text part of the response in the [Terminal UI](01_terminal_ui__ink_components_.md).
+5. Handling the "function call":
+ * Checking if it needs your permission based on the [Approval Policy](04_approval_policy___security_.md).
+ * If needed, asking you "Allow command?" via the UI.
+ * If approved, actually running the command using the [Command Execution & Sandboxing](06_command_execution___sandboxing_.md) system.
+6. Getting the result of the command (the output "hello world").
+7. Sending that result back to the AI ("I ran the command, and it printed 'hello world'").
+8. Getting the AI's final response (maybe: "Great, the script ran successfully!").
+9. Showing you the final response.
+10. Updating the conversation history with everything that happened.
+
+It's called a "loop" because it often goes back and forth between you, the AI, and tools (like the command line) until your request is fully handled.
+
+## How It Works: The Conversation Cycle
+
+The Agent Loop orchestrates a cycle:
+
+```mermaid
+graph TD
+ A[User Input] --> B[Agent Loop]
+ B --> C{Send to AI Model}
+ C --> D[AI Response: Text or Tool Call]
+ D --> B
+ B --> E{Process Response}
+ E -- Text --> F[Show Text in UI]
+ E -- Tool Call --> G{Handle Tool Call}
+ G --> H{Needs Approval?}
+ H -- Yes --> I[Ask User via UI]
+ I --> J{User Approves?}
+ H -- No --> K[Execute Tool]
+ J -- Yes --> K
+ J -- No --> L[Report Denial to AI]
+ K --> M[Get Tool Result]
+ M --> B
+ L --> B
+ F --> N[Update History]
+ M --> N
+ L --> N
+ N --> O[Ready for next Input/Step]
+```
+
+1. **Input:** Gets input from you (via the [Input Handling](02_input_handling__textbuffer_editor_.md)).
+2. **AI Call:** Sends the current conversation state (including your latest input and any previous steps) to the AI model (OpenAI API).
+3. **Response Processing:** Receives the AI's response. This could be simple text, or it could include a request to use a tool (like running a shell command). This is covered more in [Response & Tool Call Handling](05_response___tool_call_handling_.md).
+4. **Tool Handling:** If the AI requested a tool:
+ * Check the [Approval Policy](04_approval_policy___security_.md).
+ * Potentially ask you for confirmation via the [Terminal UI](01_terminal_ui__ink_components_.md).
+ * If approved, execute the tool via [Command Execution & Sandboxing](06_command_execution___sandboxing_.md).
+ * Package the tool's result (e.g., command output) to send back to the AI in the next step.
+5. **Update State:** Adds the AI's message and any tool results to the conversation history. Shows updates in the UI.
+6. **Loop:** If the task isn't finished (e.g., because a tool was used and the AI needs to react to the result), it sends the updated conversation back to the AI (Step 2). If the task *is* finished, it waits for your next input.
+
+## Using the Agent Loop (From the UI's Perspective)
+
+You don't directly interact with the `AgentLoop` class code when *using* Codex. Instead, the main UI component (`TerminalChat` in `terminal-chat.tsx`) creates and manages an `AgentLoop` instance.
+
+Think of the UI component holding the "remote control" for the Agent Loop assistant.
+
+```tsx
+// File: codex-cli/src/components/chat/terminal-chat.tsx (Highly Simplified)
+import React, { useState, useEffect, useRef } from "react";
+import { AgentLoop } from "../../utils/agent/agent-loop";
+// ... other imports: UI components, config types ...
+
+export default function TerminalChat({ config, approvalPolicy, /* ... */ }) {
+ const [items, setItems] = useState([]); // Holds conversation messages
+ const [loading, setLoading] = useState(false); // Is the assistant busy?
+ const [confirmationPrompt, setConfirmationPrompt] = useState(null); // Command to review?
+ const agentRef = useRef(null); // Holds the assistant instance
+
+ // Create the assistant when the component loads or config changes
+ useEffect(() => {
+ agentRef.current = new AgentLoop({
+ model: config.model,
+ config: config,
+ approvalPolicy: approvalPolicy,
+ // --- Callbacks: How the assistant reports back ---
+ onItem: (newItem) => { // When the assistant has a message/result
+ setItems((prev) => [...prev, newItem]); // Add it to our chat history
+ },
+ onLoading: (isLoading) => { // When the assistant starts/stops thinking
+ setLoading(isLoading);
+ },
+ getCommandConfirmation: async (command, /*...*/) => { // When the assistant needs approval
+ // Show the command in the UI and wait for user's Yes/No
+ const userDecision = await showConfirmationUI(command);
+ return { review: userDecision /* ... */ };
+ },
+ // ... other callbacks like onLastResponseId ...
+ });
+
+ return () => agentRef.current?.terminate(); // Clean up when done
+ }, [config, approvalPolicy /* ... */]);
+
+ // --- Function to send user input to the assistant ---
+ const submitInputToAgent = (userInput) => {
+ if (agentRef.current) {
+ // Tell the assistant to process this input
+ agentRef.current.run([userInput /* ... */]);
+ }
+ };
+
+ // --- UI Rendering ---
+ return (
+
+ {/* Display 'items' using TerminalMessageHistory */}
+ {/* Display input box (TerminalChatInput) or confirmationPrompt */}
+ {/* Pass `submitInputToAgent` to the input box */}
+ {/* Pass function to handle confirmation decision */}
+
+ );
+}
+```
+
+* **Initialization:** The UI creates an `AgentLoop`, giving it the necessary configuration ([Configuration Management](07_configuration_management.md)) and crucial **callback functions**. These callbacks are how the Agent Loop communicates back to the UI:
+ * `onItem`: "Here's a new message (from user, AI, or tool) to display."
+ * `onLoading`: "I'm starting/stopping my work."
+ * `getCommandConfirmation`: "I need to run this command. Please ask the user and tell me their decision."
+* **Running:** When you submit input via the ``, the UI calls the `agentRef.current.run(...)` method, handing off your request to the Agent Loop.
+* **Updates:** The Agent Loop does its work, calling the `onItem` and `onLoading` callbacks whenever something changes. The UI listens to these callbacks and updates the display accordingly (setting state variables like `items` and `loading`, which causes React to re-render).
+* **Confirmation:** If the Agent Loop needs approval, it calls `getCommandConfirmation`. The UI pauses, shows the command review prompt, waits for your decision, and then returns the decision back to the Agent Loop, which then proceeds or stops based on your choice.
+
+## Under the Hood: A Step-by-Step Flow
+
+Let's trace our "hello world" example again, focusing on the interactions:
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant InkUI as Terminal UI (Ink)
+ participant AgentLoop
+ participant OpenAI
+ participant CmdExec as Command Execution
+
+ User->>InkUI: Types "write & run hello world", presses Enter
+ InkUI->>AgentLoop: Calls `run(["write & run..."])`
+ AgentLoop->>AgentLoop: Sets loading=true (calls `onLoading(true)`)
+ InkUI->>User: Shows loading indicator
+ AgentLoop->>OpenAI: Sends request: ["write & run..."]
+ OpenAI-->>AgentLoop: Streams response: [Text: "Okay, try:", ToolCall: `shell(...)`]
+ AgentLoop->>InkUI: Calls `onItem(Text: "Okay, try:")`
+ InkUI->>User: Displays "Okay, try:"
+ AgentLoop->>AgentLoop: Processes ToolCall `shell(...)`
+ Note over AgentLoop: Checks Approval Policy
+ AgentLoop->>InkUI: Calls `getCommandConfirmation(["python", "-c", "..."])`
+ InkUI->>User: Displays "Allow command: python -c '...'?" [Yes/No]
+ User->>InkUI: Clicks/Types 'Yes'
+ InkUI-->>AgentLoop: Returns confirmation result ({ review: YES })
+ AgentLoop->>CmdExec: Executes `python -c 'print("hello world")'`
+ CmdExec-->>AgentLoop: Returns result (stdout: "hello world", exit code: 0)
+ AgentLoop->>AgentLoop: Creates `function_call_output` item
+ AgentLoop->>OpenAI: Sends request: [..., ToolCall: `shell(...)`, Output: "hello world"]
+ OpenAI-->>AgentLoop: Streams response: [Text: "Command ran successfully!"]
+ AgentLoop->>InkUI: Calls `onItem(Text: "Command ran...")`
+ InkUI->>User: Displays "Command ran successfully!"
+ AgentLoop->>AgentLoop: Sets loading=false (calls `onLoading(false)`)
+ InkUI->>User: Hides loading indicator, shows input prompt
+```
+
+This diagram shows the back-and-forth orchestration performed by the Agent Loop, coordinating between the UI, the AI model, and the command execution system.
+
+## Inside `agent-loop.ts`
+
+The core logic lives in `codex-cli/src/utils/agent/agent-loop.ts`. Let's peek at a *very* simplified structure:
+
+```typescript
+// File: codex-cli/src/utils/agent/agent-loop.ts (Simplified)
+import OpenAI from "openai";
+// ... other imports: types for config, responses, approval ...
+import { handleExecCommand } from "./handle-exec-command"; // For tool calls
+
+export class AgentLoop {
+ private oai: OpenAI; // The OpenAI client instance
+ private model: string;
+ private config: AppConfig;
+ private approvalPolicy: ApprovalPolicy;
+ // Callbacks provided by the UI:
+ private onItem: (item: ResponseItem) => void;
+ private onLoading: (loading: boolean) => void;
+ private getCommandConfirmation: (/*...*/) => Promise;
+ // ... other state like current stream, cancellation flags ...
+
+ constructor({ model, config, approvalPolicy, onItem, onLoading, getCommandConfirmation, /*...*/ }: AgentLoopParams) {
+ this.model = model;
+ this.config = config;
+ this.approvalPolicy = approvalPolicy;
+ this.onItem = onItem;
+ this.onLoading = onLoading;
+ this.getCommandConfirmation = getCommandConfirmation;
+ this.oai = new OpenAI({ /* ... API key, base URL ... */ });
+ // ... initialize other state ...
+ }
+
+ // The main method called by the UI
+ public async run(input: Array, previousResponseId: string = ""): Promise {
+ this.onLoading(true); // Signal start
+ let turnInput = input; // Input for this step of the loop
+ let lastResponseId = previousResponseId;
+
+ try {
+ // Keep looping as long as there's input (initially user msg, later tool results)
+ while (turnInput.length > 0) {
+ // 1. Send current input history to OpenAI API
+ const stream = await this.oai.responses.create({
+ model: this.model,
+ input: turnInput, // Includes user message or tool results
+ previous_response_id: lastResponseId || undefined,
+ stream: true,
+ // ... other parameters like instructions, tools ...
+ });
+
+ turnInput = []; // Clear input for the next loop iteration
+
+ // 2. Process the stream of events from OpenAI
+ for await (const event of stream) {
+ if (event.type === "response.output_item.done") {
+ const item = event.item; // Could be text, function_call, etc.
+ this.onItem(item as ResponseItem); // Send item to UI to display
+ }
+ if (event.type === "response.completed") {
+ lastResponseId = event.response.id; // Remember the ID for the next call
+ // Check the final output for tool calls
+ for (const item of event.response.output) {
+ if (item.type === "function_call") {
+ // Handle the tool call (ask for approval, execute)
+ // This might add a 'function_call_output' to `turnInput`
+ const toolResults = await this.handleFunctionCall(item);
+ turnInput.push(...toolResults);
+ }
+ }
+ }
+ // ... handle other event types ...
+ } // End stream processing
+ } // End while loop (no more input for this turn)
+ } catch (error) {
+ // ... Handle errors (network issues, API errors etc.) ...
+ this.onItem(/* Create system error message */);
+ } finally {
+ this.onLoading(false); // Signal end
+ }
+ }
+
+ // Helper to handle tool/function calls
+ private async handleFunctionCall(item: ResponseFunctionToolCall): Promise> {
+ // ... Parse arguments from 'item' ...
+ const args = /* ... parse item.arguments ... */;
+ let outputText = "Error: Unknown function";
+ let metadata = {};
+
+ if (item.name === "shell") { // Example: handle shell commands
+ // This uses the approval policy and getCommandConfirmation callback!
+ const result = await handleExecCommand(
+ args,
+ this.config,
+ this.approvalPolicy,
+ this.getCommandConfirmation,
+ /* ... cancellation signal ... */
+ );
+ outputText = result.outputText;
+ metadata = result.metadata;
+ }
+ // ... handle other function names ...
+
+ // Format the result to send back to OpenAI in the next turn
+ const outputItem: ResponseInputItem.FunctionCallOutput = {
+ type: "function_call_output",
+ call_id: item.call_id, // Link to the specific function call
+ output: JSON.stringify({ output: outputText, metadata }),
+ };
+ return [outputItem]; // This goes into `turnInput` for the next loop
+ }
+
+ // ... other methods like cancel(), terminate() ...
+}
+```
+
+* **Constructor:** Sets up the connection to OpenAI and stores the configuration and callbacks passed in by the UI.
+* **`run()`:** This is the main engine.
+ * It signals loading starts (`onLoading(true)`).
+ * It enters a `while` loop that continues as long as there's something to send to the AI (initially the user's message, later potentially the results from tools).
+ * Inside the loop, it calls `this.oai.responses.create()` to talk to the AI model, sending the current conversation turn.
+ * It processes the `stream` of events coming back from the AI.
+ * For each piece of content (`response.output_item.done`), it calls `onItem` to show it in the UI.
+ * When the AI's turn is complete (`response.completed`), it checks if the AI asked to use any tools (`function_call`).
+ * If a tool call is found, it calls `handleFunctionCall`.
+* **`handleFunctionCall()`:**
+ * Parses the details of the tool request (e.g., the command arguments).
+ * Uses `handleExecCommand` (which contains logic related to [Approval Policy](04_approval_policy___security_.md) and [Command Execution](06_command_execution___sandboxing_.md)) to potentially run the command, using the `getCommandConfirmation` callback if needed.
+ * Formats the result of the tool execution (e.g., command output) into a specific `function_call_output` message.
+ * Returns this output message. The `run` method adds this to `turnInput`, so the *next* iteration of the `while` loop will send this result back to the AI, letting it know what happened.
+* **Finally:** Once the `while` loop finishes (meaning the AI didn't request any more tools in its last response), it signals loading is done (`onLoading(false)`).
+
+This loop ensures that the conversation flows logically, handling text, tool requests, user approvals, and tool results in a structured way.
+
+## Conclusion
+
+The Agent Loop is the central orchestrator within Codex. It acts like a diligent assistant, taking your requests, interacting with the powerful AI model, managing tools like shell commands, ensuring safety through approvals, and keeping the conversation state updated. It connects the [Terminal UI](01_terminal_ui__ink_components_.md) where you interact, the [Input Handling](02_input_handling__textbuffer_editor_.md) that captures your text, the AI model that provides intelligence, and the systems that actually execute actions ([Command Execution & Sandboxing](06_command_execution___sandboxing_.md)).
+
+Understanding the Agent Loop helps you see how Codex manages the complex back-and-forth required to turn your natural language requests into concrete actions. But when the Agent Loop wants to run a command suggested by the AI, how does Codex decide whether to ask for your permission first? That crucial safety mechanism is the topic of our next chapter.
+
+Next up: [Approval Policy & Security](04_approval_policy___security_.md)
+
+---
+
+Generated by [AI Codebase Knowledge Builder](https://github.com/The-Pocket/Tutorial-Codebase-Knowledge)
\ No newline at end of file
diff --git a/docs/Codex/04_approval_policy___security.md b/docs/Codex/04_approval_policy___security.md
new file mode 100644
index 0000000..ab89565
--- /dev/null
+++ b/docs/Codex/04_approval_policy___security.md
@@ -0,0 +1,236 @@
+---
+layout: default
+title: "Approval Policy & Security"
+parent: "Codex"
+nav_order: 4
+---
+
+# Chapter 4: Approval Policy & Security
+
+In the [previous chapter](03_agent_loop.md), we saw how the **Agent Loop** acts like Codex's brain, talking to the AI and figuring out what steps to take. Sometimes, the AI might suggest actions that could change things on your computer, like modifying a file or running a command in your terminal (e.g., `git commit`, `npm install`, or even `rm important_file.txt`!).
+
+This sounds powerful, but also a little scary, right? What if the AI misunderstands and suggests deleting the wrong file? We need a way to control how much power Codex has.
+
+That's exactly what the **Approval Policy & Security** system does. It's like a security guard standing between the AI's suggestions and your actual computer.
+
+## What's the Big Idea? The Security Guard
+
+Imagine you're visiting a secure building. Depending on your pass, you have different levels of access:
+
+* **Guest Pass (`suggest` mode):** You can look around (read files), but if you want to open a door (modify a file) or use special equipment (run a command), you need to ask the guard for permission every single time.
+* **Employee Badge (`auto-edit` mode):** You can open regular office doors (modify files in the project) without asking each time, but you still need permission for restricted areas like the server room (running commands).
+* **Full Access Badge (`full-auto` mode):** You can go almost anywhere (modify files, run commands), but for potentially sensitive actions (like running commands), the guard might escort you to a special monitored room (a "sandbox") to ensure safety.
+
+The Approval Policy in Codex works just like these passes. It lets *you* choose how much autonomy Codex has when it suggests potentially risky actions.
+
+## Key Concepts: The Approval Modes
+
+Codex offers different levels of autonomy, which you can usually set with a command-line flag like `--approval-mode` or when you first configure it. These are the main modes:
+
+1. **`suggest` (Default):**
+ * **What it is:** The most cautious mode. Like the Guest Pass.
+ * **What it does:** Codex can read files to understand your project, but before it *modifies* any file or *runs* any command, it will always stop and ask for your explicit permission through the [Terminal UI](01_terminal_ui__ink_components_.md).
+ * **Use when:** You want maximum control and want to review every single change or command.
+
+2. **`auto-edit`:**
+ * **What it is:** Allows automatic file edits, but still requires approval for commands. Like the Employee Badge.
+ * **What it does:** Codex can automatically apply changes (patches) to files within your project directory. However, if it wants to run a shell command (like `npm install`, `git commit`, `python script.py`), it will still stop and ask for your permission.
+ * **Use when:** You trust the AI to make code changes but still want to manually approve any commands it tries to run.
+
+3. **`full-auto`:**
+ * **What it is:** The most autonomous mode, allowing file edits and command execution, but with safeguards. Like the Full Access Badge with escort.
+ * **What it does:** Codex can automatically apply file changes *and* run shell commands without asking you first. Crucially, to prevent accidental damage, commands run in this mode are typically executed inside a **sandbox** – a restricted environment that limits what the command can do (e.g., blocking network access, limiting file access to the project directory). We'll learn more about this in the [Command Execution & Sandboxing](06_command_execution___sandboxing.md) chapter.
+ * **Use when:** You want Codex to work as independently as possible, understanding that potentially risky commands are run with safety restrictions.
+
+## How it Works in Practice
+
+When the [Agent Loop](03_agent_loop.md) receives a suggestion from the AI to perform an action (like applying a patch or running a shell command), it doesn't just blindly execute it. Instead, it checks the current Approval Policy you've set.
+
+```mermaid
+sequenceDiagram
+ participant AgentLoop as Agent Loop
+ participant ApprovalCheck as Approval Policy Check
+ participant UserUI as Terminal UI
+ participant CmdExec as Command Execution
+
+ AgentLoop->>AgentLoop: AI suggests action (e.g., run `npm install`)
+ AgentLoop->>ApprovalCheck: Check action against policy (`auto-edit`)
+ ApprovalCheck->>ApprovalCheck: Action is `npm install` (command)
+ ApprovalCheck->>ApprovalCheck: Policy is `auto-edit` (commands need approval)
+ ApprovalCheck-->>AgentLoop: Decision: `ask-user`
+ AgentLoop->>UserUI: Request confirmation for `npm install`
+ UserUI->>UserUI: Display "Allow command `npm install`? [Y/n]"
+ UserUI-->>AgentLoop: User response (e.g., Yes)
+ AgentLoop->>CmdExec: Execute `npm install`
+```
+
+1. **Suggestion:** The AI tells the Agent Loop it wants to run `npm install`.
+2. **Check Policy:** The Agent Loop asks the Approval Policy system: "The AI wants to run `npm install`. The user set the policy to `auto-edit`. Is this okay?"
+3. **Decision:** The Approval Policy system checks its rules:
+ * The action is a shell command.
+ * The policy is `auto-edit`.
+ * Rule: In `auto-edit` mode, shell commands require user approval.
+ * Result: The decision is `ask-user`.
+4. **Ask User:** The Agent Loop receives the `ask-user` decision and uses the `getCommandConfirmation` callback (provided by the [Terminal UI](01_terminal_ui__ink_components_.md)) to display the prompt to you.
+5. **User Response:** You see the prompt and respond (e.g., 'Yes').
+6. **Execute (if approved):** The Agent Loop receives your 'Yes' and proceeds to execute the command, potentially using the [Command Execution & Sandboxing](06_command_execution___sandboxing.md) system.
+
+If the policy had been `full-auto`, the decision in Step 3 might have been `auto-approve` (with `runInSandbox: true`), and the Agent Loop would have skipped asking you (Steps 4 & 5) and gone straight to execution (Step 6), but inside the sandbox.
+
+If the action was applying a file patch and the policy was `auto-edit` or `full-auto`, the decision might also be `auto-approve` (checking if the file path is allowed), skipping the user prompt.
+
+## Under the Hood: The `approvals.ts` Logic
+
+The core logic for making these decisions lives in `codex-cli/src/approvals.ts`. A key function here is `canAutoApprove`.
+
+```typescript
+// File: codex-cli/src/approvals.ts (Simplified)
+
+// Represents the different approval modes
+export type ApprovalPolicy = "suggest" | "auto-edit" | "full-auto";
+
+// Represents the outcome of the safety check
+export type SafetyAssessment =
+ | { type: "auto-approve"; runInSandbox: boolean; reason: string; /*...*/ }
+ | { type: "ask-user"; applyPatch?: ApplyPatchCommand }
+ | { type: "reject"; reason: string };
+
+// Input for apply_patch commands
+export type ApplyPatchCommand = { patch: string; };
+
+/**
+ * Checks if a command can be run automatically based on the policy.
+ */
+export function canAutoApprove(
+ command: ReadonlyArray, // e.g., ["git", "status"] or ["apply_patch", "..."]
+ policy: ApprovalPolicy,
+ writableRoots: ReadonlyArray, // Allowed directories for edits
+ // ... env ...
+): SafetyAssessment {
+ // --- Special case: apply_patch ---
+ if (command[0] === "apply_patch") {
+ // Check if policy allows auto-editing and if patch only affects allowed files
+ const applyPatchArg = command[1] as string;
+ const patchDetails = { patch: applyPatchArg };
+
+ if (policy === "suggest") return { type: "ask-user", applyPatch: patchDetails };
+
+ if (isWritePatchConstrainedToWritablePaths(applyPatchArg, writableRoots)) {
+ return { type: "auto-approve", runInSandbox: false, reason: "Patch affects allowed files", /*...*/ };
+ }
+ // If policy is auto-edit but patch affects disallowed files, ask user.
+ // If policy is full-auto, still approve but mark for sandbox if paths are weird.
+ return policy === "full-auto" ?
+ { type: "auto-approve", runInSandbox: true, reason: "Full auto mode", /*...*/ } :
+ { type: "ask-user", applyPatch: patchDetails };
+ }
+
+ // --- Check for known safe, read-only commands ---
+ const knownSafe = isSafeCommand(command); // Checks things like "ls", "pwd", "git status"
+ if (knownSafe != null) {
+ return { type: "auto-approve", runInSandbox: false, reason: knownSafe.reason, /*...*/ };
+ }
+
+ // --- Handle shell commands (like "bash -lc 'npm install'") ---
+ // (Simplified: assumes any other command needs policy check)
+
+ // --- Default: Check policy for general commands ---
+ if (policy === "full-auto") {
+ return { type: "auto-approve", runInSandbox: true, reason: "Full auto mode", /*...*/ };
+ } else {
+ // 'suggest' and 'auto-edit' require asking for commands
+ return { type: "ask-user" };
+ }
+}
+
+// Helper to check if a command is known to be safe (read-only)
+function isSafeCommand(command: ReadonlyArray): { reason: string, group: string } | null {
+ const cmd = command[0];
+ if (["ls", "pwd", "cat", "git status", "git diff", /*...*/].includes(cmd)) {
+ return { reason: `Safe read-only command: ${cmd}`, group: "Reading" };
+ }
+ return null;
+}
+
+// Helper (simplified) to check if patch affects allowed paths
+function isWritePatchConstrainedToWritablePaths(
+ patch: string,
+ writableRoots: ReadonlyArray
+): boolean {
+ // ... logic to parse patch and check affected file paths ...
+ // ... return true if all paths are within writableRoots ...
+ return true; // Simplified for example
+}
+```
+
+* **Inputs:** `canAutoApprove` takes the command the AI wants to run (as an array of strings, like `["npm", "install"]`), the current `ApprovalPolicy` (`suggest`, `auto-edit`, or `full-auto`), and a list of directories where file edits are allowed (`writableRoots`, usually just your project's main folder).
+* **Checks:** It first handles special cases like `apply_patch` (checking the policy and file paths) and known safe, read-only commands using `isSafeCommand`.
+* **Policy Decision:** For other commands, it primarily relies on the policy:
+ * If `full-auto`, it returns `auto-approve` but sets `runInSandbox` to `true`.
+ * If `suggest` or `auto-edit`, it returns `ask-user`.
+* **Output:** It returns a `SafetyAssessment` object telling the [Agent Loop](03_agent_loop.md) what to do: `auto-approve` (and whether sandboxing is needed), `ask-user`, or in rare cases, `reject` (if the command is fundamentally invalid).
+
+This decision is then used back in the Agent Loop, often within a function like `handleExecCommand` (in `handle-exec-command.ts`), which we touched on in the previous chapter.
+
+```typescript
+// File: codex-cli/src/utils/agent/handle-exec-command.ts (Simplified snippet)
+
+import { canAutoApprove } from "../../approvals.js";
+import { ReviewDecision } from "./review.js";
+// ... other imports ...
+
+export async function handleExecCommand(
+ args: ExecInput, // Contains the command array `cmd`
+ config: AppConfig,
+ policy: ApprovalPolicy,
+ getCommandConfirmation: (/*...*/) => Promise, // UI callback
+ // ... abortSignal ...
+): Promise {
+
+ // *** Check the approval policy first! ***
+ const safety = canAutoApprove(args.cmd, policy, [process.cwd()]);
+
+ let runInSandbox: boolean;
+ switch (safety.type) {
+ case "ask-user": {
+ // Policy requires asking the user
+ const { review: decision } = await getCommandConfirmation(args.cmd, safety.applyPatch);
+ if (decision !== ReviewDecision.YES && decision !== ReviewDecision.ALWAYS) {
+ // User said No or provided feedback to stop
+ return { outputText: "aborted", metadata: { /*...*/ } };
+ }
+ // User approved! Proceed without sandbox (unless policy changes later).
+ runInSandbox = false;
+ break;
+ }
+ case "auto-approve": {
+ // Policy allows auto-approval
+ runInSandbox = safety.runInSandbox; // Respect sandbox flag from canAutoApprove
+ break;
+ }
+ case "reject": {
+ // Policy outright rejected the command
+ return { outputText: "aborted", metadata: { reason: safety.reason } };
+ }
+ }
+
+ // *** If approved (either automatically or by user), execute the command ***
+ const summary = await execCommand(args, safety.applyPatch, runInSandbox, /*...*/);
+ // ... handle results ...
+ return convertSummaryToResult(summary);
+}
+```
+
+This shows how `canAutoApprove` is called first. If it returns `ask-user`, the `getCommandConfirmation` callback (which triggers the UI prompt) is invoked. Only if the assessment is `auto-approve` or the user explicitly approves does the code proceed to actually execute the command using `execCommand`, passing the `runInSandbox` flag determined by the policy check.
+
+## Conclusion
+
+The Approval Policy & Security system is Codex's safety net. It puts you in control, letting you choose the balance between letting the AI work autonomously and requiring manual confirmation for actions that could affect your system. By understanding the `suggest`, `auto-edit`, and `full-auto` modes, you can configure Codex to operate in a way that matches your comfort level with automation and risk. This system works hand-in-hand with the [Agent Loop](03_agent_loop.md) to intercept potentially risky actions and enforce the rules you've set, sometimes using sandboxing (as we'll see later) for an extra layer of protection.
+
+Now that we know how Codex decides *whether* to perform an action, how does it actually understand the AI's response, especially when the AI wants to use a tool like running a command or applying a patch?
+
+Next up: [Response & Tool Call Handling](05_response___tool_call_handling.md)
+
+---
+
+Generated by [AI Codebase Knowledge Builder](https://github.com/The-Pocket/Tutorial-Codebase-Knowledge)
\ No newline at end of file
diff --git a/docs/Codex/05_response___tool_call_handling.md b/docs/Codex/05_response___tool_call_handling.md
new file mode 100644
index 0000000..47aeb11
--- /dev/null
+++ b/docs/Codex/05_response___tool_call_handling.md
@@ -0,0 +1,381 @@
+---
+layout: default
+title: "Response & Tool Call Handling"
+parent: "Codex"
+nav_order: 5
+---
+
+# Chapter 5: Response & Tool Call Handling
+
+In the [previous chapter](04_approval_policy___security_.md), we learned how Codex decides *if* it's allowed to perform an action suggested by the AI, acting like a security guard based on the rules you set. But how does Codex understand the AI's response in the first place, especially when the AI wants to do something specific, like run a command or change a file?
+
+That's where **Response & Tool Call Handling** comes in. Think of this part of Codex as its "ears" and "hands." It listens carefully to the instructions coming back from the AI model (the "response") and, if the AI asks to perform an action (a "tool call"), it figures out *exactly* what the AI wants to do (like which command to run or what file change to make) and gets ready to do it.
+
+## What's the Big Idea? Listening to the AI Assistant
+
+Imagine you ask your super-smart assistant (the AI model) to do something like:
+
+`codex "What's the status of my project? Use git status."`
+
+The AI doesn't just send back plain text like "Okay, I'll run it." Instead, it sends back a more structured message, almost like filling out a form:
+
+* **Text Part:** "Okay, I will check the status of your project."
+* **Action Part (Tool Call):**
+ * **Tool Name:** `shell` (meaning: use the command line)
+ * **Arguments:** `["git", "status"]` (meaning: the specific command to run)
+
+Codex needs to understand this structured response. It needs to:
+
+1. Recognize the plain text part and show it to you in the [Terminal UI](01_terminal_ui__ink_components_.md).
+2. See the "Action Part" (the Tool Call) and understand:
+ * Which tool the AI wants to use (`shell`).
+ * What specific details (arguments) are needed for that tool (`git status`).
+
+This system is crucial because it translates the AI's intent into something Codex can actually act upon.
+
+## Key Concepts
+
+1. **Structured Responses:** The OpenAI API doesn't just return a single block of text. It sends back data structured often like JSON. This allows the AI to clearly separate regular conversation text from requests to perform actions.
+
+ ```json
+ // Simplified idea of an AI response
+ {
+ "id": "response_123",
+ "output": [
+ {
+ "type": "message", // A regular text message
+ "role": "assistant",
+ "content": [{ "type": "output_text", "text": "Okay, checking the status..." }]
+ },
+ {
+ "type": "function_call", // A request to use a tool!
+ "name": "shell",
+ "arguments": "{\"command\": [\"git\", \"status\"]}", // Details for the tool
+ "call_id": "call_abc"
+ }
+ ]
+ // ... other info ...
+ }
+ ```
+ This structure makes it easy for Codex to programmatically understand the different parts of the AI's message.
+
+2. **Tool Calls (Function Calls):** When the AI wants to interact with the outside world (run a command, edit a file), it uses a special type of message in the response, often called a "function call" or "tool call". In Codex, common tool names are:
+ * `shell`: Execute a command in the terminal.
+ * `apply_patch`: Modify a file using a specific format called a "patch".
+
+3. **Arguments:** The tool call includes the necessary details, called "arguments," usually formatted as a JSON string.
+ * For the `shell` tool, the arguments specify the command to run (e.g., `{"command": ["git", "status"]}`).
+ * For the `apply_patch` tool, the arguments contain the patch text describing the file changes (e.g., `{"patch": "*** Begin Patch..."}`).
+
+## How It Works: Decoding the AI's Message
+
+When the [Agent Loop](03_agent_loop.md) receives a response from the OpenAI API, it goes through these steps:
+
+```mermaid
+sequenceDiagram
+ participant OpenAI
+ participant AgentLoop as Agent Loop
+ participant Parser as Response Parser
+ participant UI as Terminal UI
+ participant Approval as Approval Check
+
+ OpenAI-->>AgentLoop: Sends structured response (Text + Tool Call)
+ AgentLoop->>Parser: Passes raw response data
+ Parser->>Parser: Extracts Text part ("Okay...")
+ Parser-->>AgentLoop: Returns extracted Text
+ AgentLoop->>UI: Sends Text to display ("onItem" callback)
+ Parser->>Parser: Extracts Tool Call part (shell, ["git", "status"])
+ Parser-->>AgentLoop: Returns Tool Name ("shell") & Arguments (["git", "status"])
+ AgentLoop->>Approval: Sends Tool details for policy check
+ Note over Approval: Next step: Chapter 4/6
+```
+
+1. **Receive Response:** The [Agent Loop](03_agent_loop.md) gets the structured response data from the OpenAI API.
+2. **Parse:** It uses helper functions (often found in `utils/parsers.ts`) to examine the response structure.
+3. **Extract Text:** If there's a regular text message (`"type": "message"`), it's extracted and sent to the [Terminal UI](01_terminal_ui__ink_components_.md) via the `onItem` callback to be displayed.
+4. **Extract Tool Call:** If there's a tool call (`"type": "function_call"`):
+ * The **tool name** (e.g., `shell`) is identified.
+ * The **arguments** string is extracted.
+ * The arguments string (which is often JSON) is parsed to get the actual details (e.g., the `command` array `["git", "status"]`).
+5. **Prepare for Action:** The Agent Loop now knows the specific tool and its arguments. It packages this information (tool name + parsed arguments) and prepares for the next stage: checking the [Approval Policy & Security](04_approval_policy___security_.md) and, if approved, proceeding to [Command Execution & Sandboxing](06_command_execution___sandboxing_.md).
+
+## Under the Hood: Parsing the Details
+
+Let's look at simplified code snippets showing how this parsing happens.
+
+### In the Agent Loop (`agent-loop.ts`)
+
+The `AgentLoop` processes events streamed from the OpenAI API. When a complete response arrives or a specific tool call item is identified, it needs handling.
+
+```typescript
+// File: codex-cli/src/utils/agent/agent-loop.ts (Simplified)
+
+// Inside the loop processing OpenAI stream events...
+for await (const event of stream) {
+ if (event.type === "response.output_item.done") {
+ const item = event.item; // Could be text, function_call, etc.
+ this.onItem(item as ResponseItem); // Send to UI
+
+ // If it's a tool call, mark it for later processing
+ if (item.type === "function_call") {
+ // Store item.call_id or item details
+ // to handle after the stream finishes
+ }
+ }
+
+ if (event.type === "response.completed") {
+ // Process the full response output once the stream is done
+ for (const item of event.response.output) {
+ if (item.type === "function_call") {
+ // *** This is where we handle the tool call! ***
+ // Calls a helper function like handleFunctionCall
+ const toolResults = await this.handleFunctionCall(item);
+ // Prepare results to potentially send back to AI
+ turnInput.push(...toolResults);
+ }
+ }
+ lastResponseId = event.response.id;
+ }
+ // ... other event types ...
+}
+
+// Helper function to process the tool call details
+private async handleFunctionCall(item: ResponseFunctionToolCall): Promise> {
+ const name = item.name; // e.g., "shell"
+ const rawArguments = item.arguments; // e.g., "{\"command\": [\"git\", \"status\"]}"
+ const callId = item.call_id;
+
+ // *** Use a parser to get structured arguments ***
+ const args = parseToolCallArguments(rawArguments ?? "{}"); // From parsers.ts
+
+ if (args == null) {
+ // Handle error: arguments couldn't be parsed
+ return [/* error output item */];
+ }
+
+ let outputText = `Error: Unknown function ${name}`;
+ let metadata = {};
+
+ // Check which tool was called
+ if (name === "shell") {
+ // *** Prepare for execution ***
+ // Call handleExecCommand, which checks approval and runs the command
+ const result = await handleExecCommand(
+ args, // Contains { cmd: ["git", "status"], ... }
+ this.config,
+ this.approvalPolicy,
+ this.getCommandConfirmation, // Function to ask user via UI
+ /* ... cancellation signal ... */
+ );
+ outputText = result.outputText;
+ metadata = result.metadata;
+ } else if (name === "apply_patch") {
+ // Similar logic, potentially using execApplyPatch after approval check
+ // It would parse args.patch using logic from parse-apply-patch.ts
+ }
+ // ... other tools ...
+
+ // Create the result message to send back to the AI
+ const outputItem: ResponseInputItem.FunctionCallOutput = {
+ type: "function_call_output",
+ call_id: callId,
+ output: JSON.stringify({ output: outputText, metadata }),
+ };
+ return [outputItem];
+}
+```
+
+* The loop iterates through the response `output` items.
+* If an item is a `function_call`, the `handleFunctionCall` helper is called.
+* `handleFunctionCall` extracts the `name` and `arguments`.
+* It crucially calls `parseToolCallArguments` (from `utils/parsers.ts`) to turn the JSON string `arguments` into a usable object.
+* Based on the `name` (`shell`, `apply_patch`), it calls the appropriate execution handler (like `handleExecCommand`), passing the parsed arguments. This handler coordinates with the [Approval Policy & Security](04_approval_policy___security_.md) and [Command Execution & Sandboxing](06_command_execution___sandboxing_.md) systems.
+
+### In the Parsers (`parsers.ts`)
+
+This file contains helpers to decode the tool call details.
+
+```typescript
+// File: codex-cli/src/utils/parsers.ts (Simplified)
+import { formatCommandForDisplay } from "src/format-command.js";
+// ... other imports ...
+
+/**
+ * Parses the raw JSON string from a tool call's arguments.
+ * Expects specific shapes for known tools like 'shell'.
+ */
+export function parseToolCallArguments(
+ rawArguments: string,
+): ExecInput | undefined { // ExecInput contains { cmd, workdir, timeoutInMillis }
+ let json: unknown;
+ try {
+ json = JSON.parse(rawArguments); // Basic JSON parsing
+ } catch (err) {
+ // Handle JSON parse errors
+ return undefined;
+ }
+
+ if (typeof json !== "object" || json == null) return undefined;
+
+ // Look for 'command' or 'cmd' property, expecting an array of strings
+ const { cmd, command, patch /* other possible args */ } = json as Record;
+ const commandArray = toStringArray(cmd) ?? toStringArray(command);
+
+ // If it's a shell command, require the command array
+ if (commandArray != null) {
+ return {
+ cmd: commandArray,
+ // Optional: extract workdir and timeout too
+ workdir: typeof (json as any).workdir === "string" ? (json as any).workdir : undefined,
+ timeoutInMillis: typeof (json as any).timeout === "number" ? (json as any).timeout : undefined,
+ };
+ }
+
+ // If it's an apply_patch command, require the patch string
+ if (typeof patch === 'string') {
+ // Return a structure indicating it's a patch, maybe:
+ // return { type: 'patch', patch: patch }; // Or incorporate into ExecInput if unified
+ // For simplicity here, let's assume handleFunctionCall routes based on name,
+ // so we might just return the raw parsed JSON for patch.
+ // But a structured return is better. Let's adapt ExecInput slightly for demo:
+ return { cmd: ['apply_patch'], patch: patch }; // Use a placeholder cmd
+ }
+
+ return undefined; // Unknown or invalid arguments structure
+}
+
+// Helper to check if an object is an array of strings
+function toStringArray(obj: unknown): Array | undefined {
+ if (Array.isArray(obj) && obj.every((item) => typeof item === "string")) {
+ return obj as Array;
+ }
+ return undefined;
+}
+
+/**
+ * Parses a full FunctionCall item for display/review purposes.
+ */
+export function parseToolCall(
+ toolCall: ResponseFunctionToolCall,
+): CommandReviewDetails | undefined { // CommandReviewDetails has { cmd, cmdReadableText, ... }
+ // Use the argument parser
+ const args = parseToolCallArguments(toolCall.arguments);
+ if (args == null) return undefined;
+
+ // Format the command nicely for display
+ const cmdReadableText = formatCommandForDisplay(args.cmd);
+
+ // ... potentially add auto-approval info ...
+
+ return {
+ cmd: args.cmd,
+ cmdReadableText: cmdReadableText,
+ // ... other details ...
+ };
+}
+```
+
+* `parseToolCallArguments` takes the raw JSON string (`{"command": ["git", "status"]}`) and uses `JSON.parse`.
+* It then checks if the parsed object has the expected structure (e.g., a `command` property that is an array of strings for `shell`, or a `patch` string for `apply_patch`).
+* It returns a structured object (`ExecInput`) containing the validated arguments, or `undefined` if parsing fails.
+* `parseToolCall` uses `parseToolCallArguments` and then formats the command nicely for display using `formatCommandForDisplay`.
+
+### Handling Patches (`parse-apply-patch.ts`)
+
+When the tool is `apply_patch`, the arguments contain a multi-line string describing the changes. Codex has specific logic to parse this format.
+
+```typescript
+// File: codex-cli/src/utils/agent/parse-apply-patch.ts (Conceptual)
+
+// Defines types like ApplyPatchOp (create, delete, update)
+
+export function parseApplyPatch(patch: string): Array | null {
+ // 1. Check for "*** Begin Patch" and "*** End Patch" markers.
+ if (!patch.startsWith("*** Begin Patch\n") || !patch.endsWith("\n*** End Patch")) {
+ return null; // Invalid format
+ }
+
+ // 2. Extract the body between the markers.
+ const patchBody = /* ... extract body ... */;
+ const lines = patchBody.split('\n');
+
+ const operations: Array = [];
+ for (const line of lines) {
+ // 3. Check for operation markers:
+ if (line.startsWith("*** Add File: ")) {
+ operations.push({ type: "create", path: /* path */, content: "" });
+ } else if (line.startsWith("*** Delete File: ")) {
+ operations.push({ type: "delete", path: /* path */ });
+ } else if (line.startsWith("*** Update File: ")) {
+ operations.push({ type: "update", path: /* path */, update: "", added: 0, deleted: 0 });
+ } else if (operations.length > 0) {
+ // 4. If inside an operation, parse the content/diff lines (+/-)
+ const lastOp = operations[operations.length - 1];
+ // ... add line content to create/update operation ...
+ } else {
+ // Invalid line outside of an operation
+ return null;
+ }
+ }
+
+ return operations; // Return the list of parsed operations
+}
+```
+
+This parser specifically understands the `*** Add File:`, `*** Delete File:`, `*** Update File:` markers and the `+`/`-` lines within patches to figure out exactly which files to change and how.
+
+### Displaying Tool Calls (`terminal-chat-response-item.tsx`)
+
+The UI needs to show tool calls differently from regular messages.
+
+```tsx
+// File: codex-cli/src/components/chat/terminal-chat-response-item.tsx (Simplified)
+import { parseToolCall } from "../../utils/parsers";
+// ... other imports: Box, Text from ink ...
+
+export default function TerminalChatResponseItem({ item }: { item: ResponseItem }): React.ReactElement {
+ switch (item.type) {
+ case "message":
+ // ... render regular message ...
+ break;
+ case "function_call": // <-- Handle tool calls
+ return ;
+ case "function_call_output":
+ // ... render tool output ...
+ break;
+ // ... other cases ...
+ }
+ // ... fallback ...
+}
+
+function TerminalChatResponseToolCall({ message }: { message: ResponseFunctionToolCallItem }) {
+ // Use the parser to get displayable details
+ const details = parseToolCall(message); // From parsers.ts
+
+ if (!details) return Invalid tool call ;
+
+ return (
+
+ command
+ {/* Display the nicely formatted command */}
+ $ {details.cmdReadableText}
+
+ );
+}
+```
+
+* The main component checks the `item.type`.
+* If it's `function_call`, it renders a specific component (`TerminalChatResponseToolCall`).
+* This component uses `parseToolCall` (from `utils/parsers.ts`) to get the details and displays the command in a distinct style (e.g., with a `$` prefix and magenta color).
+
+## Conclusion
+
+You've now seen how Codex acts as an interpreter for the AI. It doesn't just receive text; it receives structured instructions. The **Response & Tool Call Handling** system is responsible for parsing these instructions, figuring out if the AI wants to use a tool (like `shell` or `apply_patch`), and extracting the precise arguments needed for that tool. This crucial step translates the AI's intentions into actionable details that Codex can then use to interact with your system, always respecting the rules set by the [Approval Policy & Security](04_approval_policy___security_.md).
+
+Now that Codex understands *what* command the AI wants to run (e.g., `git status`), how does it actually *execute* that command safely, especially if running in `full-auto` mode? That's the topic of our next chapter.
+
+Next up: [Command Execution & Sandboxing](06_command_execution___sandboxing.md)
+
+---
+
+Generated by [AI Codebase Knowledge Builder](https://github.com/The-Pocket/Tutorial-Codebase-Knowledge)
\ No newline at end of file
diff --git a/docs/Codex/06_command_execution___sandboxing.md b/docs/Codex/06_command_execution___sandboxing.md
new file mode 100644
index 0000000..6086814
--- /dev/null
+++ b/docs/Codex/06_command_execution___sandboxing.md
@@ -0,0 +1,360 @@
+---
+layout: default
+title: "Command Execution & Sandboxing"
+parent: "Codex"
+nav_order: 6
+---
+
+# Chapter 6: Command Execution & Sandboxing
+
+In the [previous chapter](05_response___tool_call_handling.md), we learned how Codex listens to the AI and understands when it wants to use a tool, like running a specific shell command (`git status` or `npm install`). We also know from the [Approval Policy & Security](04_approval_policy___security_.md) chapter that Codex checks if it *should* run the command based on your chosen safety level.
+
+But once Codex has the command and permission (either from you or automatically), how does it actually *run* that command? And how does it do it safely, especially if you've given it more freedom in `full-auto` mode?
+
+That's the job of the **Command Execution & Sandboxing** system.
+
+## What's the Big Idea? The Workshop Safety Zones
+
+Imagine Codex is working in a workshop. This system is like the different areas and safety procedures in that workshop:
+
+* **The Main Workbench (Raw Execution):** For simple, safe tasks (like running `ls` to list files), Codex might just use the tools directly on the main workbench. It's straightforward, but you wouldn't use dangerous chemicals there.
+* **The Safety Cage (Sandboxing):** For potentially risky tasks (like testing a powerful new tool, or maybe running a command the AI suggested that you haven't manually approved in `full-auto` mode), Codex moves the work inside a special safety cage. This cage has reinforced walls and maybe limited power outlets, preventing any accidents from affecting the rest of the workshop.
+
+This system takes a command requested by the AI (like `python script.py` or `git commit -m "AI commit"`) and actually runs it on your computer's command line. Crucially, it decides *whether* to run it directly (on the workbench) or inside a restricted environment (the safety cage or "sandbox"). It also collects the results – what the command printed (output/stdout), any errors (stderr), and whether it finished successfully (exit code).
+
+## Key Concepts
+
+1. **Raw Execution:**
+ * **What:** Running the command directly using your system's shell, just like you would type it.
+ * **When:** Used for commands deemed safe, or when you explicitly approve a command in `suggest` or `auto-edit` mode.
+ * **Pros:** Simple, has full access to your environment (which might be needed).
+ * **Cons:** If the AI makes a mistake and suggests a harmful command, running it raw could cause problems.
+
+2. **Sandboxing:**
+ * **What:** Running the command inside a restricted environment that limits what it can do. Think of it as putting the command in "jail."
+ * **How (Examples):**
+ * **macOS Seatbelt:** Uses a built-in macOS feature (`sandbox-exec`) with a specific policy file to strictly control what the command can access (e.g., only allow writing to the project folder, block network access).
+ * **Docker Container:** Runs the command inside a lightweight container (like the one defined in `codex-cli/Dockerfile`). This container has only specific tools installed and can have network rules applied (using `iptables`/`ipset` via `init_firewall.sh`) to limit internet access.
+ * **When:** Typically used automatically in `full-auto` mode (as decided by the [Approval Policy & Security](04_approval_policy___security_.md) check), or potentially if a specific command is flagged as needing extra caution.
+ * **Pros:** Significantly reduces the risk of accidental damage from faulty or malicious commands suggested by the AI.
+ * **Cons:** Might prevent a command from working if it legitimately needs access to something the sandbox blocks (like a specific system file or network resource). The setup can be more complex.
+
+## How It Works: From Approval to Execution
+
+The Command Execution system doesn't decide *whether* to run a command – that's the job of the [Approval Policy & Security](04_approval_policy___security_.md). This system comes into play *after* the approval check.
+
+Remember the `handleExecCommand` function from the [Agent Loop](03_agent_loop.md) chapter? It first calls `canAutoApprove` ([Approval Policy & Security](04_approval_policy___security_.md)). If the command is approved (either by policy or by you), `canAutoApprove` tells `handleExecCommand` *whether* sandboxing is needed (`runInSandbox: true` or `runInSandbox: false`).
+
+```typescript
+// File: codex-cli/src/utils/agent/handle-exec-command.ts (Simplified Snippet)
+
+import { execCommand } from "./exec-command-helper"; // (Conceptual helper name)
+import { getSandbox } from "./sandbox-selector"; // (Conceptual helper name)
+// ... other imports: canAutoApprove, config, policy types ...
+
+async function handleExecCommand(
+ args: ExecInput, // Contains { cmd: ["git", "status"], ... }
+ config: AppConfig,
+ policy: ApprovalPolicy,
+ getCommandConfirmation: (/*...*/) => Promise,
+ // ... abortSignal ...
+): Promise {
+
+ // 1. Check policy (calls canAutoApprove)
+ const safety = canAutoApprove(command, policy, [process.cwd()]);
+ let runInSandbox: boolean;
+
+ // 2. Determine if approved and if sandbox needed
+ switch (safety.type) {
+ case "ask-user":
+ // Ask user via getCommandConfirmation...
+ // If approved, runInSandbox = false;
+ break;
+ case "auto-approve":
+ runInSandbox = safety.runInSandbox; // Get sandbox flag from policy check
+ break;
+ // ... handle reject ...
+ }
+
+ // 3. *** Execute the command! ***
+ // Determine the actual sandbox mechanism (Seatbelt, Docker, None)
+ const sandboxType = await getSandbox(runInSandbox);
+ // Call the function that handles execution
+ const summary = await execCommand(
+ args,
+ applyPatch, // (if it was an apply_patch command)
+ sandboxType,
+ abortSignal,
+ );
+
+ // 4. Format and return results
+ return convertSummaryToResult(summary);
+}
+```
+
+* **Steps 1 & 2:** Approval policy is checked, maybe the user is asked. We get the `runInSandbox` boolean.
+* **Step 3:** A helper (`getSandbox`) determines the specific `SandboxType` (e.g., `MACOS_SEATBELT` or `NONE`) based on `runInSandbox` and the operating system. Then, the core execution function (`execCommand`) is called, passing the command details and the chosen `sandboxType`.
+* **Step 4:** The results (stdout, stderr, exit code) from `execCommand` are packaged up.
+
+## Under the Hood: Running the Command
+
+Let's trace the execution flow:
+
+```mermaid
+sequenceDiagram
+ participant HEC as handleExecCommand
+ participant EC as execCommand (Helper)
+ participant Exec as exec (exec.ts)
+ participant Raw as rawExec (raw-exec.ts)
+ participant SB as execWithSeatbelt (macos-seatbelt.ts)
+
+ HEC->>EC: Run `git status`, sandboxType=NONE
+ EC->>Exec: Calls exec({cmd: ["git", "status"], ...}, SandboxType.NONE)
+ Exec->>Exec: Selects rawExec based on sandboxType
+ Exec->>Raw: Calls rawExec(["git", "status"], ...)
+ Raw->>NodeJS: Uses child_process.spawn("git", ["status"], ...)
+ NodeJS-->>Raw: Command finishes (stdout, stderr, code)
+ Raw-->>Exec: Returns result
+ Exec-->>EC: Returns result
+ EC-->>HEC: Returns final summary
+
+ %% Example with Sandbox %%
+ HEC->>EC: Run `dangerous_script.sh`, sandboxType=MACOS_SEATBELT
+ EC->>Exec: Calls exec({cmd: ["dangerous..."], ...}, SandboxType.MACOS_SEATBELT)
+ Exec->>Exec: Selects execWithSeatbelt based on sandboxType
+ Exec->>SB: Calls execWithSeatbelt(["dangerous..."], ...)
+ SB->>SB: Constructs `sandbox-exec` command with policy
+ SB->>Raw: Calls rawExec(["sandbox-exec", "-p", policy, "--", "dangerous..."], ...)
+ Raw->>NodeJS: Uses child_process.spawn("sandbox-exec", [...])
+ NodeJS-->>Raw: Sandboxed command finishes (stdout, stderr, code)
+ Raw-->>SB: Returns result
+ SB-->>Exec: Returns result
+ Exec-->>EC: Returns result
+ EC-->>HEC: Returns final summary
+```
+
+### The Entry Point: `exec.ts`
+
+This file acts as a router. It takes the command and the desired `SandboxType` and calls the appropriate execution function.
+
+```typescript
+// File: codex-cli/src/utils/agent/exec.ts (Simplified)
+import type { ExecInput, ExecResult, SandboxType } from "./sandbox/interface.js";
+import { execWithSeatbelt } from "./sandbox/macos-seatbelt.js";
+import { exec as rawExec } from "./sandbox/raw-exec.js";
+// ... other imports like process_patch for apply_patch ...
+
+// Never rejects, maps errors to non-zero exit code / stderr
+export function exec(
+ { cmd, workdir, timeoutInMillis }: ExecInput,
+ sandbox: SandboxType, // e.g., NONE, MACOS_SEATBELT
+ abortSignal?: AbortSignal,
+): Promise {
+
+ // Decide which execution function to use
+ const execFunction =
+ sandbox === SandboxType.MACOS_SEATBELT ? execWithSeatbelt : rawExec;
+
+ const opts: SpawnOptions = { /* ... set timeout, workdir ... */ };
+ const writableRoots = [process.cwd(), os.tmpdir()]; // Basic allowed paths
+
+ // Call the chosen function (either raw or sandboxed)
+ return execFunction(cmd, opts, writableRoots, abortSignal);
+}
+
+// Special handler for apply_patch pseudo-command
+export function execApplyPatch(patchText: string): ExecResult {
+ try {
+ // Use file system operations directly (fs.writeFileSync etc.)
+ const result = process_patch(/* ... patchText, fs functions ... */);
+ return { stdout: result, stderr: "", exitCode: 0 };
+ } catch (error: unknown) {
+ // Handle errors during patching
+ return { stdout: "", stderr: String(error), exitCode: 1 };
+ }
+}
+```
+
+* It receives the command (`cmd`), options (`workdir`, `timeout`), and the `sandbox` type.
+* It checks the `sandbox` type and chooses either `execWithSeatbelt` (for macOS sandbox) or `rawExec` (for direct execution).
+* It calls the selected function.
+* Note: `apply_patch` is handled specially by `execApplyPatch`, which directly uses Node.js file system functions instead of spawning a shell command.
+
+### Raw Execution: `raw-exec.ts`
+
+This function runs the command directly using Node.js's built-in `child_process.spawn`.
+
+```typescript
+// File: codex-cli/src/utils/agent/sandbox/raw-exec.ts (Simplified)
+import type { ExecResult } from "./interface";
+import { spawn, type SpawnOptions } from "child_process";
+import { log, isLoggingEnabled } from "../log.js";
+
+const MAX_BUFFER = 1024 * 100; // 100 KB limit for stdout/stderr
+
+// Never rejects, maps errors to non-zero exit code / stderr
+export function exec(
+ command: Array, // e.g., ["git", "status"]
+ options: SpawnOptions,
+ _writableRoots: Array, // Not used in raw exec
+ abortSignal?: AbortSignal,
+): Promise {
+ const prog = command[0];
+ const args = command.slice(1);
+
+ return new Promise((resolve) => {
+ // Spawn the child process
+ const child = spawn(prog, args, {
+ ...options,
+ stdio: ["ignore", "pipe", "pipe"], // Don't wait for stdin, capture stdout/err
+ detached: true, // Allows killing process group on abort
+ });
+
+ // Handle abort signal if provided
+ if (abortSignal) {
+ // Add listener to kill child process if aborted
+ // ... abort handling logic ...
+ }
+
+ let stdout = "";
+ let stderr = "";
+ // Capture stdout/stderr, respecting MAX_BUFFER limit
+ child.stdout?.on("data", (data) => { /* append to stdout if under limit */ });
+ child.stderr?.on("data", (data) => { /* append to stderr if under limit */ });
+
+ // Handle process exit
+ child.on("exit", (code, signal) => {
+ resolve({ stdout, stderr, exitCode: code ?? 1 });
+ });
+
+ // Handle errors like "command not found"
+ child.on("error", (err) => {
+ resolve({ stdout: "", stderr: String(err), exitCode: 1 });
+ });
+ });
+}
+```
+
+* It uses `child_process.spawn` to run the command. `spawn` is generally safer than `exec` as it doesn't involve an intermediate shell unless explicitly requested.
+* It captures `stdout` and `stderr` data, enforcing a maximum buffer size to prevent memory issues.
+* It listens for the `exit` event to get the exit code.
+* It listens for the `error` event (e.g., if the command executable doesn't exist).
+* It includes logic to kill the child process if the `abortSignal` is triggered (e.g., user presses Ctrl+C).
+* Crucially, it always `resolve`s the promise, even on errors, packaging the error into the `ExecResult`.
+
+### Sandboxing on macOS: `macos-seatbelt.ts`
+
+This function wraps the command execution using macOS's `sandbox-exec` tool.
+
+```typescript
+// File: codex-cli/src/utils/agent/sandbox/macos-seatbelt.ts (Simplified)
+import type { ExecResult } from "./interface.js";
+import { exec as rawExec } from "./raw-exec.js"; // Uses raw exec internally!
+import { log } from "../log.js";
+
+const READ_ONLY_POLICY_BASE = `
+(version 1)
+(deny default)
+(allow file-read*) ; Allow reading most things
+(allow process-exec process-fork signal) ; Allow running/forking
+(allow sysctl-read) ; Allow reading system info
+; ... more base rules ...
+`;
+
+// Runs command inside macOS Seatbelt sandbox
+export function execWithSeatbelt(
+ cmd: Array, // The original command e.g., ["python", "script.py"]
+ opts: SpawnOptions,
+ writableRoots: Array, // Dirs allowed for writing, e.g., project root
+ abortSignal?: AbortSignal,
+): Promise {
+
+ // 1. Build the sandbox policy string
+ let policy = READ_ONLY_POLICY_BASE;
+ let policyParams: Array = [];
+ if (writableRoots.length > 0) {
+ // Add rules to allow writing ONLY within specified roots
+ const writeRules = writableRoots.map(
+ (root, i) => `(allow file-write* (subpath (param "WR_${i}")))`
+ ).join("\n");
+ policy += `\n${writeRules}`;
+ // Create parameters for sandbox-exec
+ policyParams = writableRoots.map((root, i) => `-DWR_${i}=${root}`);
+ }
+ log(`Seatbelt Policy: ${policy}`);
+
+ // 2. Construct the actual command to run: sandbox-exec + policy + original command
+ const fullCommand = [
+ "sandbox-exec",
+ "-p", policy, // Pass the policy string
+ ...policyParams, // Pass parameters like -DWR_0=/path/to/project
+ "--", // End of sandbox-exec options
+ ...cmd, // The original command and arguments
+ ];
+
+ // 3. Execute the `sandbox-exec` command using rawExec
+ return rawExec(fullCommand, opts, [], abortSignal); // writableRoots not needed by rawExec here
+}
+```
+
+* It defines a base Seatbelt policy (`.sb` file format) that denies most actions by default but allows basic read operations and process execution.
+* It dynamically adds `allow file-write*` rules for the specific `writableRoots` provided (usually the project directory and temp directories).
+* It constructs a new command line that starts with `sandbox-exec`, passes the generated policy (`-p`), passes parameters defining the writable roots (`-D`), and finally appends the original command.
+* It then calls `rawExec` to run this *entire* `sandbox-exec ... -- original-command ...` line. The operating system handles enforcing the sandbox rules.
+
+### Sandboxing with Docker: `Dockerfile`
+
+Another approach, often used on Linux or as a fallback, is Docker. The `Dockerfile` defines the restricted environment.
+
+```dockerfile
+# File: codex-cli/Dockerfile (Simplified Snippets)
+
+# Start from a basic Node.js image
+FROM node:20
+
+# Install only necessary tools (git, jq, rg, maybe python/bash, etc.)
+# Avoid installing powerful tools unless absolutely needed.
+RUN apt update && apt install -y \
+ git jq ripgrep sudo iproute2 iptables ipset \
+ # ... other minimal tools ...
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
+
+# Copy codex itself into the container
+COPY dist/codex.tgz codex.tgz
+RUN npm install -g codex.tgz
+
+# Setup non-root user
+USER node
+WORKDIR /home/node/workspace # Work happens here
+
+# Copy and set up firewall script (runs via sudo)
+# This script uses iptables/ipset to block network access by default,
+# potentially allowing only specific domains if configured.
+COPY scripts/init_firewall.sh /usr/local/bin/
+USER root
+RUN chmod +x /usr/local/bin/init_firewall.sh && \
+ # Allow 'node' user to run firewall script via sudo without password
+ echo "node ALL=(root) NOPASSWD: /usr/local/bin/init_firewall.sh" > /etc/sudoers.d/node-firewall
+USER node
+
+# Default command when container starts (might be codex or just a shell)
+# ENTRYPOINT ["codex"]
+```
+
+* **Minimal Tools:** The Docker image includes only a limited set of command-line tools, reducing the potential attack surface.
+* **Non-Root User:** Commands run as a non-privileged user (`node`) inside the container.
+* **Workspace:** Work typically happens in a specific directory (e.g., `/home/node/workspace`), often mapped to your project directory on the host machine.
+* **Network Firewall:** An `init_firewall.sh` script (run via `sudo` at startup or when needed) configures `iptables` to restrict network access. This prevents sandboxed commands from easily calling out to arbitrary internet addresses.
+* **Usage:** Codex might be run *entirely* within this container, or it might invoke commands *inside* this container from the outside using `docker exec`.
+
+## Conclusion
+
+You've reached the end of the workshop tour! The **Command Execution & Sandboxing** system is Codex's way of actually *doing* things on the command line when instructed by the AI. It carefully considers the safety level decided by the [Approval Policy & Security](04_approval_policy___security_.md) and chooses the right execution method: direct "raw" execution for trusted commands, or running inside a protective "sandbox" (like macOS Seatbelt or a Docker container) for potentially riskier operations, especially in `full-auto` mode. This layered approach allows Codex to be powerful while providing crucial safety mechanisms against unintended consequences.
+
+We've seen how Codex handles input, talks to the AI, checks policies, and executes commands. But how does Codex know *which* AI model to use, what your API key is, or which approval mode you prefer? All these settings need to be managed.
+
+Next up: [Configuration Management](07_configuration_management.md)
+
+---
+
+Generated by [AI Codebase Knowledge Builder](https://github.com/The-Pocket/Tutorial-Codebase-Knowledge)
\ No newline at end of file
diff --git a/docs/Codex/07_configuration_management.md b/docs/Codex/07_configuration_management.md
new file mode 100644
index 0000000..d4207fa
--- /dev/null
+++ b/docs/Codex/07_configuration_management.md
@@ -0,0 +1,310 @@
+---
+layout: default
+title: "Configuration Management"
+parent: "Codex"
+nav_order: 7
+---
+
+# Chapter 7: Configuration Management
+
+In the [previous chapter](06_command_execution___sandboxing_.md), we saw how Codex carefully executes commands, using sandboxing for safety when needed. But how does Codex remember your preferences between sessions? For instance, how does it know which AI model you like to use, or whether you prefer `auto-edit` mode? And how can you give Codex persistent instructions about how you want it to behave?
+
+This is where **Configuration Management** comes in. Think of it like the settings menu or preferences file for Codex.
+
+## What's the Big Idea? Remembering Your Settings
+
+Imagine you prefer using the powerful `gpt-4o` model instead of the default `o4-mini`. Or perhaps you always want Codex to follow a specific coding style or avoid using certain commands unless you explicitly ask. It would be annoying to tell Codex this *every single time* you run it using command-line flags like `--model gpt-4o`.
+
+Configuration Management solves this by allowing Codex to:
+
+1. **Load Default Settings:** Read a special file to know your preferred model, default [Approval Policy](04_approval_policy___security_.md) mode, etc.
+2. **Load Custom Instructions:** Read other special files containing your personal guidelines or project-specific rules for the AI.
+
+This way, Codex behaves consistently according to your setup without needing constant reminders. It's like setting up your favorite text editor with your preferred theme and plugins – you do it once, and it remembers.
+
+## Key Concepts
+
+1. **Configuration File (`config.yaml`)**:
+ * **Where:** Lives in your home directory, inside a hidden folder: `~/.codex/config.yaml` (it might also be `.json` or `.yml`).
+ * **What:** Stores your default settings. The most common setting is the AI `model` you want Codex to use. You can also set things like the default error handling behavior in `full-auto` mode (`fullAutoErrorMode`).
+ * **Format:** Usually written in YAML (or JSON), which is a simple, human-readable format.
+
+2. **Instruction Files (`instructions.md`, `codex.md`)**:
+ * **Where:**
+ * **Global:** `~/.codex/instructions.md` - These instructions apply every time you run Codex, anywhere on your system.
+ * **Project-Specific:** `codex.md` (or `.codex.md`) - Placed in the root directory of your code project (or sometimes in subdirectories). These instructions apply only when you run Codex within that specific project.
+ * **What:** Contain text instructions (written in Markdown) that guide the AI's behavior. Think of it as giving your AI assistant standing orders.
+ * **Format:** Plain Markdown text.
+
+3. **Loading Order:** Codex combines these instructions intelligently:
+ * It first reads the global instructions (`~/.codex/instructions.md`).
+ * Then, if it finds a project-specific `codex.md` in your current working directory (or its parent Git repository root), it adds those instructions too. This lets project-specific rules override or add to your global ones.
+
+## How to Use It: Setting Your Preferences
+
+Let's make Codex always use `gpt-4o` and give it a global instruction.
+
+**1. Set the Default Model:**
+
+Create or edit the file `~/.codex/config.yaml` (you might need to create the `.codex` directory first). Add the following content:
+
+```yaml
+# File: ~/.codex/config.yaml
+
+# Use the gpt-4o model by default for all Codex runs
+model: gpt-4o
+
+# Optional: How to handle errors when running commands in full-auto
+# fullAutoErrorMode: ask-user # (Default) Ask user what to do
+# fullAutoErrorMode: ignore-and-continue # Don't stop on error
+```
+
+* **Explanation:** This simple YAML file tells Codex that your preferred `model` is `gpt-4o`. Now, you don't need to type `--model gpt-4o` every time!
+
+**2. Add Global Instructions:**
+
+Create or edit the file `~/.codex/instructions.md`. Add some guidelines:
+
+```markdown
+# File: ~/.codex/instructions.md
+
+- Always explain your reasoning step-by-step before suggesting code or commands.
+- Prefer using Python for scripting tasks unless otherwise specified.
+- Use emojis in your responses! 🎉
+```
+
+* **Explanation:** This Markdown file gives the AI assistant general rules to follow during *any* conversation.
+
+**3. (Optional) Add Project Instructions:**
+
+Navigate to your project's root directory (e.g., `~/my-cool-project/`) and create a file named `codex.md`:
+
+```markdown
+# File: ~/my-cool-project/codex.md
+
+- This project uses TypeScript and adheres to the Prettier style guide.
+- When adding new features, always include unit tests using Jest.
+- Do not run `git push` directly; always suggest creating a pull request.
+```
+
+* **Explanation:** When you run `codex` inside `~/my-cool-project/`, the AI will get *both* the global instructions *and* these project-specific ones.
+
+Now, when you run `codex` (without any flags overriding these settings), it will automatically:
+
+* Use the `gpt-4o` model.
+* Receive the combined instructions (global + project-specific, if applicable) to guide its responses and actions.
+
+You can disable loading the project `codex.md` file by using the `--no-project-doc` flag if needed.
+
+## Under the Hood: How Codex Loads Configuration
+
+When you start the Codex CLI, one of the first things it does is figure out its configuration.
+
+```mermaid
+sequenceDiagram
+ participant CLI as Codex CLI Process
+ participant ConfigLoader as config.ts (loadConfig)
+ participant FileSystem as Your Computer's Files
+
+ CLI->>ConfigLoader: Start: Call loadConfig()
+ ConfigLoader->>FileSystem: Check for ~/.codex/config.yaml (or .json, .yml)?
+ FileSystem-->>ConfigLoader: Found config.yaml
+ ConfigLoader->>FileSystem: Read ~/.codex/config.yaml
+ FileSystem-->>ConfigLoader: YAML content (e.g., model: gpt-4o)
+ ConfigLoader->>ConfigLoader: Parse YAML, store model='gpt-4o'
+ ConfigLoader->>FileSystem: Check for ~/.codex/instructions.md?
+ FileSystem-->>ConfigLoader: Found instructions.md
+ ConfigLoader->>FileSystem: Read ~/.codex/instructions.md
+ FileSystem-->>ConfigLoader: Global instructions text
+ ConfigLoader->>FileSystem: Check for project 'codex.md' (discoverProjectDocPath)?
+ FileSystem-->>ConfigLoader: Found project/codex.md
+ ConfigLoader->>FileSystem: Read project/codex.md
+ FileSystem-->>ConfigLoader: Project instructions text
+ ConfigLoader->>ConfigLoader: Combine global + project instructions
+ ConfigLoader-->>CLI: Return AppConfig object { model, instructions }
+ CLI->>CLI: Use AppConfig for AgentLoop, etc.
+```
+
+1. **Start:** The main CLI process (`cli.tsx`) starts up.
+2. **Load Config:** It calls the `loadConfig` function (from `utils/config.ts`).
+3. **Read Settings:** `loadConfig` looks for `~/.codex/config.yaml` (or `.json`/`.yml`). If found, it reads the file, parses the YAML/JSON, and stores the settings (like `model`). If not found, it uses defaults (like `o4-mini`).
+4. **Read Global Instructions:** It looks for `~/.codex/instructions.md`. If found, it reads the content.
+5. **Find Project Instructions:** It calls helper functions like `discoverProjectDocPath` to search the current directory and parent directories (up to the Git root) for a `codex.md` file.
+6. **Read Project Instructions:** If `codex.md` is found, it reads the content.
+7. **Combine:** `loadConfig` concatenates the global and project instructions (if any) into a single string.
+8. **Return:** It returns an `AppConfig` object containing the final model choice, the combined instructions, and other settings.
+9. **Use Config:** The CLI process then uses this `AppConfig` object when setting up the [Agent Loop](03_agent_loop.md) and other parts of the application.
+
+## Diving into Code (`config.ts`)
+
+The magic happens mainly in `codex-cli/src/utils/config.ts`.
+
+Here's how the CLI entry point (`cli.tsx`) uses `loadConfig`:
+
+```typescript
+// File: codex-cli/src/cli.tsx (Simplified)
+
+import { loadConfig } from "./utils/config";
+import App from "./app";
+// ... other imports: React, render, meow ...
+
+// --- Get command line arguments ---
+const cli = meow(/* ... cli setup ... */);
+const prompt = cli.input[0];
+const modelOverride = cli.flags.model; // e.g., --model gpt-4
+
+// --- Load Configuration ---
+// loadConfig handles reading files and combining instructions
+let config = loadConfig(
+ undefined, // Use default config file paths
+ undefined, // Use default instructions file paths
+ {
+ cwd: process.cwd(), // Where are we running from? (for project docs)
+ disableProjectDoc: Boolean(cli.flags.noProjectDoc), // Did user pass --no-project-doc?
+ projectDocPath: cli.flags.projectDoc as string | undefined, // Explicit project doc?
+ }
+);
+
+// --- Apply Overrides ---
+// Command-line flags take precedence over config file settings
+config = {
+ ...config, // Start with loaded config
+ model: modelOverride ?? config.model, // Use flag model if provided, else keep loaded one
+ apiKey: process.env["OPENAI_API_KEY"] || "", // Get API key from environment
+};
+
+// --- Check Model Support ---
+// ... check if config.model is valid ...
+
+// --- Render the App ---
+// Pass the final, combined config object to the main UI component
+const instance = render(
+ ,
+);
+```
+
+* **Explanation:** The code first calls `loadConfig`, passing options related to finding the project `codex.md`. It then merges these loaded settings with any overrides provided via command-line flags (like `--model`). The final `config` object is passed to the main React `` component.
+
+Inside `config.ts`, the loading logic looks something like this:
+
+```typescript
+// File: codex-cli/src/utils/config.ts (Simplified)
+
+import { existsSync, readFileSync } from "fs";
+import { load as loadYaml } from "js-yaml";
+import { homedir } from "os";
+import { join, dirname, resolve as resolvePath } from "path";
+
+export const CONFIG_DIR = join(homedir(), ".codex");
+export const CONFIG_YAML_FILEPATH = join(CONFIG_DIR, "config.yaml");
+// ... other paths: .json, .yml, instructions.md ...
+export const DEFAULT_AGENTIC_MODEL = "o4-mini";
+
+// Represents full runtime config
+export type AppConfig = {
+ apiKey?: string;
+ model: string;
+ instructions: string;
+ // ... other settings ...
+};
+
+// Options for loading
+export type LoadConfigOptions = {
+ cwd?: string;
+ disableProjectDoc?: boolean;
+ projectDocPath?: string;
+ isFullContext?: boolean; // Affects default model choice
+};
+
+export const loadConfig = (
+ configPath: string | undefined = CONFIG_YAML_FILEPATH, // Default path
+ instructionsPath: string | undefined = join(CONFIG_DIR, "instructions.md"),
+ options: LoadConfigOptions = {},
+): AppConfig => {
+ let storedConfig: Record = {}; // Holds data from config.yaml
+
+ // 1. Find and read config.yaml/.json/.yml
+ let actualConfigPath = /* ... logic to find existing config file ... */ ;
+ if (existsSync(actualConfigPath)) {
+ try {
+ const raw = readFileSync(actualConfigPath, "utf-8");
+ // Parse based on file extension (.yaml, .yml, .json)
+ storedConfig = /* ... parse YAML or JSON ... */ raw;
+ } catch { /* ignore parse errors */ }
+ }
+
+ // 2. Read global instructions.md
+ const userInstructions = existsSync(instructionsPath)
+ ? readFileSync(instructionsPath, "utf-8")
+ : "";
+
+ // 3. Read project codex.md (if enabled)
+ let projectDoc = "";
+ if (!options.disableProjectDoc /* ... and env var check ... */) {
+ const cwd = options.cwd ?? process.cwd();
+ // loadProjectDoc handles discovery and reading the file
+ projectDoc = loadProjectDoc(cwd, options.projectDocPath);
+ }
+
+ // 4. Combine instructions
+ const combinedInstructions = [userInstructions, projectDoc]
+ .filter((s) => s?.trim()) // Remove empty strings
+ .join("\n\n--- project-doc ---\n\n"); // Join with separator
+
+ // 5. Determine final model (use stored, else default)
+ const model = storedConfig.model?.trim()
+ ? storedConfig.model.trim()
+ : (options.isFullContext ? /* full ctx default */ : DEFAULT_AGENTIC_MODEL);
+
+ // 6. Assemble the final config object
+ const config: AppConfig = {
+ model: model,
+ instructions: combinedInstructions,
+ // ... merge other settings from storedConfig ...
+ };
+
+ // ... First-run bootstrap logic to create default files if missing ...
+
+ return config;
+};
+
+// Helper to find and read project doc
+function loadProjectDoc(cwd: string, explicitPath?: string): string {
+ const filepath = explicitPath
+ ? resolvePath(cwd, explicitPath)
+ : discoverProjectDocPath(cwd); // Search logic
+
+ if (!filepath || !existsSync(filepath)) return "";
+
+ try {
+ const buf = readFileSync(filepath);
+ // Limit size, return content
+ return buf.slice(0, /* MAX_BYTES */).toString("utf-8");
+ } catch { return ""; }
+}
+
+// Helper to find codex.md by walking up directories
+function discoverProjectDocPath(startDir: string): string | null {
+ // ... logic to check current dir, then walk up to git root ...
+ // ... checks for codex.md, .codex.md etc. ...
+ return /* path or null */;
+}
+```
+
+* **Explanation:** `loadConfig` reads the YAML/JSON config file, reads the global `instructions.md`, uses helpers like `loadProjectDoc` and `discoverProjectDocPath` to find and read the project-specific `codex.md`, combines the instructions, determines the final model name (using defaults if necessary), and returns everything in a structured `AppConfig` object.
+
+## Conclusion
+
+Configuration Management makes Codex much more convenient and personalized. By reading settings from `~/.codex/config.yaml` and instructions from `~/.codex/instructions.md` and project-specific `codex.md` files, it remembers your preferences (like your favorite AI model) and follows your standing orders without you needing to repeat them every time. This allows for a smoother and more consistent interaction tailored to your workflow and project needs.
+
+So far, we've mostly seen Codex working interactively in a chat-like loop. But what if you want Codex to perform a task and exit, perhaps as part of a script?
+
+Next up: [Single-Pass Mode](08_single_pass_mode.md)
+
+---
+
+Generated by [AI Codebase Knowledge Builder](https://github.com/The-Pocket/Tutorial-Codebase-Knowledge)
\ No newline at end of file
diff --git a/docs/Codex/08_single_pass_mode.md b/docs/Codex/08_single_pass_mode.md
new file mode 100644
index 0000000..5ed458c
--- /dev/null
+++ b/docs/Codex/08_single_pass_mode.md
@@ -0,0 +1,313 @@
+---
+layout: default
+title: "Single-Pass Mode"
+parent: "Codex"
+nav_order: 8
+---
+
+# Chapter 8: Single-Pass Mode
+
+In the [previous chapter](07_configuration_management.md), we explored how Codex uses configuration files to remember your preferences and follow custom instructions. We've mostly seen Codex operate in its default interactive mode, like having a conversation in the [Terminal UI](01_terminal_ui__ink_components_.md) where the [Agent Loop](03_agent_loop.md) goes back and forth with the AI.
+
+But what if you have a task that's very clearly defined? Imagine you want to rename a function across your entire project. You know exactly what needs to be done, and you don't really need a back-and-forth chat. Wouldn't it be faster if you could just give Codex the instructions and have it figure out *all* the necessary changes at once?
+
+That's exactly the idea behind **Single-Pass Mode**.
+
+## What's the Big Idea? The Architect Analogy
+
+Think about building a house. The normal, interactive mode of Codex is like having a conversation with your architect room by room: "Let's design the kitchen." "Okay, now how about the living room?" "Should we add a window here?". It's collaborative and allows for adjustments along the way.
+
+**Single-Pass Mode** is different. It's like giving the architect the complete blueprints, all the requirements, and the site survey *upfront*, and asking them to come back with the *final, complete building plan* in one go.
+
+In this experimental mode, Codex tries to:
+
+1. Gather a large amount of context about your project (lots of code files).
+2. Send your request *and* all that context to the AI model *at the same time*.
+3. Ask the AI to generate a *complete set* of file operations (creations, updates, deletions) needed to fulfill your request, all in a single response.
+4. Show you the proposed changes for review.
+5. If you approve, apply all the changes and exit.
+
+This mode aims for efficiency, especially on larger, well-defined tasks where you're reasonably confident the AI can generate the full solution without needing clarification.
+
+## Key Concepts
+
+1. **Full Context (Within Limits):** Instead of just looking at one or two files, Codex gathers the content of many files in your project (respecting ignore rules from [Configuration Management](07_configuration_management.md) and size limits like `MAX_CONTEXT_CHARACTER_LIMIT`). This gives the AI a broader view of your codebase.
+2. **Single Structured Response:** The AI isn't just asked for text. It's specifically instructed to respond with a structured list of *all* the file operations required. Codex uses a predefined schema (like `EditedFilesSchema` defined using Zod in `file_ops.ts`) to tell the AI exactly how to format this list.
+3. **All-or-Nothing Confirmation:** You are presented with a summary and a diff (showing additions and deletions) of *all* the proposed changes across all affected files. You then give a single "Yes" or "No" to apply everything or nothing.
+4. **Efficiency for Defined Tasks:** This mode shines when your instructions are clear and the task doesn't likely require interactive refinement (e.g., "Rename function X to Y everywhere", "Add logging to every public method in class Z").
+
+## How to Use It
+
+You typically invoke single-pass mode using a specific command-line flag when running Codex (the exact flag might vary, but let's assume `--single-pass`).
+
+**Example:**
+
+Let's say you want to rename a function `calculate_total` to `compute_grand_total` throughout your project located in `~/my-sales-app/`.
+
+```bash
+cd ~/my-sales-app/
+codex --single-pass "Rename the function 'calculate_total' to 'compute_grand_total' in all project files."
+```
+
+**What Happens:**
+
+1. **Context Loading:** Codex will identify the files in `~/my-sales-app/` (respecting ignores), read their content, and note the size. You might see output indicating this.
+2. **AI Thinking:** It sends your prompt and the file contents to the AI, asking for the complete set of changes. You'll likely see a spinner.
+3. **Review:** Codex receives the proposed file operations from the AI. It calculates the differences (diffs) and shows you a summary:
+ ```
+ Summary:
+ Modified: src/utils.py (+1/-1)
+ Modified: tests/test_utils.py (+1/-1)
+ Modified: main_app.py (+1/-1)
+
+ Proposed Diffs:
+ ================================================================================
+ Changes for: src/utils.py
+ --------------------------------------------------------------------------------
+ @@ -10,7 +10,7 @@
+ # ... code ...
+
+ -def calculate_total(items):
+ +def compute_grand_total(items):
+ # ... implementation ...
+
+ # ... (more diffs for other files) ...
+
+ Apply these changes? [y/N]
+ ```
+4. **Confirmation:** You type `y` and press Enter.
+5. **Applying:** Codex modifies the files `src/utils.py`, `tests/test_utils.py`, and `main_app.py` according to the diffs.
+6. **Exit:** The Codex process finishes.
+
+If you had typed `n`, no files would have been changed.
+
+## Under the Hood: The Single-Pass Flow
+
+Let's trace the journey when you run `codex --single-pass "prompt"`:
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant CLI as Codex CLI (SinglePass)
+ participant ContextLoader as context_files.ts
+ participant OpenAI
+ participant FileSystem
+
+ User->>CLI: Runs `codex --single-pass "Rename func..."`
+ CLI->>ContextLoader: Get project file contents (respecting ignores)
+ ContextLoader->>FileSystem: Reads relevant files
+ FileSystem-->>ContextLoader: File contents
+ ContextLoader-->>CLI: Returns list of files & content
+ CLI->>CLI: Formats huge prompt (request + file contents) using `renderTaskContext`
+ CLI->>OpenAI: Sends single large request (expecting structured `EditedFilesSchema` response)
+ Note over CLI, OpenAI: AI processes context and request
+ OpenAI-->>CLI: Returns structured response { ops: [ {path:..., updated_full_content:...}, ... ] }
+ CLI->>CLI: Parses the `ops` list (`file_ops.ts`)
+ CLI->>CLI: Generates diffs and summary (`code_diff.ts`)
+ CLI->>User: Displays summary & diffs, asks "Apply changes? [y/N]"
+ User->>CLI: Types 'y'
+ CLI->>FileSystem: Applies changes (writes updated content, creates/deletes files)
+ CLI->>User: Shows "Changes applied." message
+ CLI->>CLI: Exits
+```
+
+1. **Invocation:** The CLI (`cli_singlepass.tsx`) is started in single-pass mode.
+2. **Context Gathering:** It uses functions like `getFileContents` from `utils/singlepass/context_files.ts` to read the content of project files, respecting ignore patterns and size limits.
+3. **Prompt Construction:** It builds a large prompt using `renderTaskContext` from `utils/singlepass/context.ts`. This prompt includes your request and embeds the content of all gathered files, often in an XML-like format.
+4. **AI Call:** It sends this single, massive prompt to the OpenAI API. Crucially, it tells the API to format the response according to a specific structure (`EditedFilesSchema` from `utils/singlepass/file_ops.ts`) which expects a list of file operations.
+5. **Response Parsing:** The CLI receives the response and uses the `EditedFilesSchema` to parse the expected list of operations (create file, update file content, delete file, move file).
+6. **Diffing & Summary:** It uses helpers like `generateDiffSummary` and `generateEditSummary` from `utils/singlepass/code_diff.ts` to compare the proposed `updated_full_content` for each operation against the original file content, generating human-readable diffs and a summary.
+7. **Confirmation:** The main application component (`SinglePassApp` in `components/singlepass-cli-app.tsx`) displays the summary and diffs using Ink components and prompts the user for confirmation (`ConfirmationPrompt`).
+8. **Application:** If confirmed, the `applyFileOps` function iterates through the parsed operations and uses Node.js's `fs.promises` module (`fsPromises.writeFile`, `fsPromises.unlink`, etc.) to modify the files on disk.
+9. **Exit:** The application cleans up and exits.
+
+## Diving into Code
+
+Let's look at the key parts involved.
+
+### Starting Single-Pass Mode (`cli_singlepass.tsx`)
+
+This module likely provides the entry point function called by the main CLI when the `--single-pass` flag is detected.
+
+```typescript
+// File: codex-cli/src/cli_singlepass.tsx (Simplified)
+import type { AppConfig } from "./utils/config";
+import { SinglePassApp } from "./components/singlepass-cli-app";
+import { render } from "ink";
+import React from "react";
+
+// This function is called by the main CLI logic
+export async function runSinglePass({
+ originalPrompt, // The user's request string
+ config, // Loaded configuration (model, instructions)
+ rootPath, // The project directory
+}: { /* ... */ }): Promise {
+ return new Promise((resolve) => {
+ // Render the dedicated Ink UI for single-pass mode
+ render(
+ resolve()} // Callback when the app is done
+ />,
+ );
+ });
+}
+```
+
+* **Explanation:** This function simply renders the main React component (`SinglePassApp`) responsible for the entire single-pass UI and logic, passing along the user's prompt and configuration. It uses a Promise to signal when the process is complete.
+
+### The Main UI and Logic (`singlepass-cli-app.tsx`)
+
+This component manages the state (loading, thinking, confirming, etc.) and orchestrates the single-pass flow.
+
+```typescript
+// File: codex-cli/src/components/singlepass-cli-app.tsx (Simplified Snippets)
+import React, { useEffect, useState } from "react";
+import { Box, Text, useApp } from "ink";
+import OpenAI from "openai";
+import { zodResponseFormat } from "openai/helpers/zod";
+// --- Local Utils ---
+import { getFileContents } from "../utils/singlepass/context_files";
+import { renderTaskContext } from "../utils/singlepass/context";
+import { EditedFilesSchema, FileOperation } from "../utils/singlepass/file_ops";
+import { generateDiffSummary, generateEditSummary } from "../utils/singlepass/code_diff";
+import * as fsPromises from "fs/promises";
+// --- UI Components ---
+import { InputPrompt, ConfirmationPrompt } from "./prompts"; // Conceptual grouping
+
+export function SinglePassApp({ /* ...props: config, rootPath, onExit ... */ }): JSX.Element {
+ const app = useApp();
+ const [state, setState] = useState("init"); // 'init', 'prompt', 'thinking', 'confirm', 'applied', 'error'...
+ const [files, setFiles] = useState([]); // Holds { path, content }
+ const [diffInfo, setDiffInfo] = useState({ summary: "", diffs: "", ops: [] });
+
+ // 1. Load file context on mount
+ useEffect(() => {
+ (async () => {
+ const fileContents = await getFileContents(rootPath, /* ignorePatterns */);
+ setFiles(fileContents);
+ setState("prompt"); // Ready for user input
+ })();
+ }, [rootPath]);
+
+ // 2. Function to run the AI task
+ async function runSinglePassTask(userPrompt: string) {
+ setState("thinking");
+ try {
+ // Format the context + prompt for the AI
+ const taskContextStr = renderTaskContext({ prompt: userPrompt, files, /*...*/ });
+
+ const openai = new OpenAI({ /* ... config ... */ });
+ // Call OpenAI, specifying the expected structured response format
+ const chatResp = await openai.beta.chat.completions.parse({
+ model: config.model,
+ messages: [{ role: "user", content: taskContextStr }],
+ response_format: zodResponseFormat(EditedFilesSchema, "schema"), // Ask for this specific structure!
+ });
+
+ const edited = chatResp.choices[0]?.message?.parsed; // The parsed { ops: [...] } object
+
+ if (!edited || !Array.isArray(edited.ops)) { /* Handle no ops */ }
+
+ // Generate diffs from the AI's proposed operations
+ const [combinedDiffs, opsToApply] = generateDiffSummary(edited, /* original files map */);
+ if (!opsToApply.length) { /* Handle no actual changes */ }
+
+ const summary = generateEditSummary(opsToApply, /* original files map */);
+ setDiffInfo({ summary, diffs: combinedDiffs, ops: opsToApply });
+ setState("confirm"); // Move to confirmation state
+
+ } catch (err) { setState("error"); }
+ }
+
+ // 3. Function to apply the changes
+ async function applyFileOps(ops: Array) {
+ for (const op of ops) {
+ if (op.delete) {
+ await fsPromises.unlink(op.path).catch(() => {});
+ } else { // Create or Update
+ const newContent = op.updated_full_content || "";
+ await fsPromises.mkdir(path.dirname(op.path), { recursive: true });
+ await fsPromises.writeFile(op.path, newContent, "utf-8");
+ }
+ // Handle move_to separately if needed
+ }
+ setState("applied");
+ }
+
+ // --- Render logic based on `state` ---
+ if (state === "prompt") {
+ return ;
+ }
+ if (state === "thinking") { /* Show Spinner */ }
+ if (state === "confirm") {
+ return (
+
+ {/* Display diffInfo.summary and diffInfo.diffs */}
+ {
+ if (accept) applyFileOps(diffInfo.ops);
+ else setState("skipped");
+ }}
+ />
+
+ );
+ }
+ if (state === "applied") { /* Show success, maybe offer another prompt */ }
+ // ... other states: init, error, skipped ...
+
+ return ... ; // Fallback
+}
+```
+
+* **Explanation:** This component uses `useEffect` to load files initially. The `runSinglePassTask` function orchestrates calling the AI (using `zodResponseFormat` to enforce the `EditedFilesSchema`) and generating diffs. `applyFileOps` performs the actual file system changes if the user confirms via the `ConfirmationPrompt`. The UI rendered depends heavily on the current `state`.
+
+### Defining the AI's Output: `file_ops.ts`
+
+This file defines the exact structure Codex expects the AI to return in single-pass mode.
+
+```typescript
+// File: codex-cli/src/utils/singlepass/file_ops.ts (Simplified)
+import { z } from "zod"; // Zod is a schema validation library
+
+// Schema for a single file operation
+export const FileOperationSchema = z.object({
+ path: z.string().describe("Absolute path to the file."),
+ updated_full_content: z.string().optional().describe(
+ "FULL CONTENT of the file after modification. MUST provide COMPLETE content."
+ ),
+ delete: z.boolean().optional().describe("Set true to delete the file."),
+ move_to: z.string().optional().describe("New absolute path if file is moved."),
+ // Ensure only one action per operation (update, delete, or move)
+}).refine(/* ... validation logic ... */);
+
+// Schema for the overall response containing a list of operations
+export const EditedFilesSchema = z.object({
+ ops: z.array(FileOperationSchema).describe("List of file operations."),
+});
+
+export type FileOperation = z.infer;
+export type EditedFiles = z.infer;
+```
+
+* **Explanation:** This uses the Zod library to define a strict schema. `FileOperationSchema` describes a single change (update, delete, or move), emphasizing that `updated_full_content` must be the *entire* file content. `EditedFilesSchema` wraps this in a list called `ops`. This schema is given to the OpenAI API (via `zodResponseFormat`) to ensure the AI's response is structured correctly.
+
+### Generating Context and Diffs
+
+* **`context.ts` (`renderTaskContext`):** Takes the user prompt and file contents and formats them into the large string sent to the AI, including instructions and often wrapping file content in XML-like tags (`... ... `).
+* **`code_diff.ts` (`generateDiffSummary`, `generateEditSummary`):** Takes the `ops` returned by the AI and compares the `updated_full_content` with the original content read from disk. It uses a library (like `diff`) to generate standard diff text and then formats it (often with colors) and creates a short summary list for display.
+
+## Conclusion
+
+Single-Pass Mode offers a different, potentially faster way to use Codex for well-defined tasks. By providing extensive context upfront and asking the AI for a complete set of structured file operations in one response, it minimizes back-and-forth. You gather context, send one big request, review the complete proposed solution, and either accept or reject it entirely. While still experimental, it's a powerful approach for streamlining larger refactoring or generation tasks where the requirements are clear.
+
+This concludes our tour through the core concepts of Codex! We've journeyed from the [Terminal UI](01_terminal_ui__ink_components_.md) and [Input Handling](02_input_handling__textbuffer_editor_.md), through the central [Agent Loop](03_agent_loop.md), into the crucial aspects of [Approval Policy & Security](04_approval_policy___security_.md), [Response & Tool Call Handling](05_response___tool_call_handling.md), and safe [Command Execution & Sandboxing](06_command_execution___sandboxing.md), learned about [Configuration Management](07_configuration_management.md), and finally explored the alternative [Single-Pass Mode](08_single_pass_mode.md).
+
+We hope this gives you a solid understanding of how Codex works under the hood. Feel free to dive deeper into the codebase, experiment, and perhaps even contribute!
+
+---
+
+Generated by [AI Codebase Knowledge Builder](https://github.com/The-Pocket/Tutorial-Codebase-Knowledge)
\ No newline at end of file
diff --git a/docs/Codex/index.md b/docs/Codex/index.md
new file mode 100644
index 0000000..9ae7ac4
--- /dev/null
+++ b/docs/Codex/index.md
@@ -0,0 +1,37 @@
+---
+layout: default
+title: "Codex"
+nav_order: 5
+has_children: true
+---
+
+# Tutorial: Codex
+
+> This tutorial is AI-generated! To learn more, check out [AI Codebase Knowledge Builder](https://github.com/The-Pocket/Tutorial-Codebase-Knowledge)
+
+Codex[View Repo](https://github.com/openai/codex) is a command-line interface (CLI) tool that functions as an **AI coding assistant**.
+It runs in your terminal, allowing you to chat with an AI model (like *GPT-4o*) to understand, modify, and generate code within your projects.
+The tool can read files, apply changes (*patches*), and execute shell commands, prioritizing safety through user **approval policies** and command **sandboxing**. It supports both interactive chat and a non-interactive *single-pass mode* for batch operations.
+
+```mermaid
+flowchart TD
+ A0["Agent Loop"]
+ A1["Terminal UI (Ink Components)"]
+ A2["Approval Policy & Security"]
+ A3["Command Execution & Sandboxing"]
+ A4["Configuration Management"]
+ A5["Response & Tool Call Handling"]
+ A6["Single-Pass Mode"]
+ A7["Input Handling (TextBuffer/Editor)"]
+ A0 -- "Drives updates for" --> A1
+ A0 -- "Processes responses via" --> A5
+ A0 -- "Consults policy from" --> A2
+ A0 -- "Loads config using" --> A4
+ A1 -- "Uses editor for input" --> A7
+ A2 -- "Dictates sandboxing for" --> A3
+ A4 -- "Provides settings to" --> A2
+ A5 -- "Triggers" --> A3
+ A7 -- "Provides user input to" --> A0
+ A0 -- "Can initiate" --> A6
+ A6 -- "Renders via specific UI" --> A1
+```
\ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
index a8e0532..f5d87c9 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -24,6 +24,7 @@ This is a tutorial project of [Pocket Flow](https://github.com/The-Pocket/Pocket
- [Browser Use](./Browser Use/index.md) - Let AI surf the web for you, clicking buttons and filling forms like a digital assistant!
- [Celery](./Celery/index.md) - Supercharge your app with background tasks that run while you sleep!
- [Click](./Click/index.md) - Turn Python functions into slick command-line tools with just a decorator!
+- [Codex](./Codex/index.md) - Turn plain English into working code with this AI terminal wizard!
- [Crawl4AI](./Crawl4AI/index.md) - Train your AI to extract exactly what matters from any website!
- [CrewAI](./CrewAI/index.md) - Assemble a dream team of AI specialists to tackle impossible problems!
- [DSPy](./DSPy/index.md) - Build LLM apps like Lego blocks that optimize themselves!