codexapi 0.12.9__tar.gz → 0.12.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. {codexapi-0.12.9/src/codexapi.egg-info → codexapi-0.12.11}/PKG-INFO +24 -9
  2. {codexapi-0.12.9 → codexapi-0.12.11}/README.md +23 -8
  3. {codexapi-0.12.9 → codexapi-0.12.11}/pyproject.toml +1 -1
  4. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/__init__.py +3 -2
  5. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/agent.py +153 -18
  6. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/async_agent.py +15 -16
  7. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/cli.py +9 -8
  8. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/lead.py +1 -1
  9. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/task.py +1 -1
  10. {codexapi-0.12.9 → codexapi-0.12.11/src/codexapi.egg-info}/PKG-INFO +24 -9
  11. {codexapi-0.12.9 → codexapi-0.12.11}/tests/test_agent_backend.py +51 -2
  12. {codexapi-0.12.9 → codexapi-0.12.11}/LICENSE +0 -0
  13. {codexapi-0.12.9 → codexapi-0.12.11}/setup.cfg +0 -0
  14. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/__main__.py +0 -0
  15. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/agents.py +0 -0
  16. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/foreach.py +0 -0
  17. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/gh_integration.py +0 -0
  18. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/pushover.py +0 -0
  19. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/ralph.py +0 -0
  20. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/rate_limits.py +0 -0
  21. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/science.py +0 -0
  22. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/taskfile.py +0 -0
  23. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi/welfare.py +0 -0
  24. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi.egg-info/SOURCES.txt +0 -0
  25. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi.egg-info/dependency_links.txt +0 -0
  26. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi.egg-info/entry_points.txt +0 -0
  27. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi.egg-info/requires.txt +0 -0
  28. {codexapi-0.12.9 → codexapi-0.12.11}/src/codexapi.egg-info/top_level.txt +0 -0
  29. {codexapi-0.12.9 → codexapi-0.12.11}/tests/test_agents.py +0 -0
  30. {codexapi-0.12.9 → codexapi-0.12.11}/tests/test_async_agent.py +0 -0
  31. {codexapi-0.12.9 → codexapi-0.12.11}/tests/test_rate_limits.py +0 -0
  32. {codexapi-0.12.9 → codexapi-0.12.11}/tests/test_science.py +0 -0
  33. {codexapi-0.12.9 → codexapi-0.12.11}/tests/test_task_progress.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: codexapi
3
- Version: 0.12.9
3
+ Version: 0.12.11
4
4
  Summary: Minimal Python API for running the Codex CLI.
5
5
  License: MIT
6
6
  Keywords: codex,agent,cli,openai
@@ -80,6 +80,8 @@ Use `backend="cursor"` (or set `CODEXAPI_BACKEND=cursor`) to switch to the
80
80
  Cursor agent backend.
81
81
  Use `fast=True` in Codex API calls, or `--fast` in the CLI, to opt into Codex
82
82
  fast mode. Normal mode is the default.
83
+ Use `model="..."` and `thinking="..."` in Codex API calls to override the
84
+ backend model and reasoning effort for a run.
83
85
 
84
86
  ## CLI
85
87
 
@@ -155,7 +157,8 @@ Resume a session and print the thread/session id to stderr:
155
157
  codexapi run --thread-id THREAD_ID --print-thread-id "Continue where we left off."
156
158
  ```
157
159
 
158
- Use `--no-yolo` to disable `--yolo` (Codex uses `--full-auto`).
160
+ Use `--no-yolo` to keep unattended operation with Codex auto approvals, without
161
+ forcing a sandbox policy.
159
162
  Use `--include-thinking` to return all agent messages joined together for `codexapi run` (Codex only).
160
163
 
161
164
  Lead mode periodically checks in on a long-running agent session with the
@@ -303,7 +306,8 @@ codexapi ralph --cancel --cwd /path/to/project
303
306
  ```
304
307
 
305
308
  Science mode wraps a short task in a science prompt and runs it through the
306
- Ralph loop. It defaults to `--yolo` and expects progress notes in `SCIENCE.md`.
309
+ Ralph loop. It defaults to dangerous no-sandbox automation and expects progress
310
+ notes in `SCIENCE.md`.
307
311
  Each iteration appends the agent output to `LOGBOOK.md` and the runner extracts
308
312
  any improved figures of merit for optional notifications. You can also set
309
313
  `--max-duration` to stop after the current iteration once a time limit is hit.
@@ -336,33 +340,41 @@ codexapi foreach list.txt task.yaml --retry-all
336
340
 
337
341
  ## API
338
342
 
339
- ### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False) -> str`
343
+ ### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False, model=None, thinking=None) -> str`
340
344
 
341
345
  Runs a single agent turn and returns only the agent's message. Any reasoning
342
346
  items are filtered out.
343
347
 
344
348
  - `prompt` (str): prompt to send to the agent backend.
345
349
  - `cwd` (str | PathLike | None): working directory for the agent session.
346
- - `yolo` (bool): pass `--yolo` when true (defaults to true).
350
+ - `yolo` (bool): use the backend's most permissive unattended mode when true
351
+ (defaults to true). For Codex, `False` uses auto approvals without forcing a
352
+ sandbox policy.
347
353
  - `flags` (str | None): extra CLI flags to pass to the agent backend.
348
354
  - `include_thinking` (bool): when true, return all agent messages joined.
349
355
  - `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
350
356
  - `fast` (bool): enable Codex fast mode (defaults to normal mode).
357
+ - `model` (str | None): backend model override. Codex maps this to `-c model=...`; Cursor maps it to `--model ...`.
358
+ - `thinking` (str | None): Codex reasoning effort override, mapped to `-c model_reasoning_effort=...`.
351
359
 
352
- ### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False)`
360
+ ### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False, model=None, thinking=None)`
353
361
 
354
362
  Creates a stateful session wrapper. Calling the instance sends the prompt into
355
363
  the same conversation and returns only the agent's message.
356
364
 
357
365
  - `__call__(prompt) -> str`: send a prompt to the agent backend and return the message.
358
366
  - `thread_id -> str | None`: expose the underlying session id once created.
359
- - `yolo` (bool): pass `--yolo` when true (defaults to true).
367
+ - `yolo` (bool): use the backend's most permissive unattended mode when true
368
+ (defaults to true). For Codex, `False` uses auto approvals without forcing a
369
+ sandbox policy.
360
370
  - `flags` (str | None): extra CLI flags to pass to the agent backend.
361
371
  - `welfare` (bool): when true, append welfare stop instructions to each prompt
362
372
  and raise `WelfareStop` if the agent outputs `MAKE IT STOP`.
363
373
  - `include_thinking` (bool): when true, return all agent messages joined.
364
374
  - `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
365
375
  - `fast` (bool): enable Codex fast mode (defaults to normal mode).
376
+ - `model` (str | None): backend model override.
377
+ - `thinking` (str | None): Codex reasoning effort override.
366
378
  For Cursor, `thread_id` corresponds to the `session_id` returned by the agent.
367
379
 
368
380
  ### `lead(minutes, prompt, cwd=None, yolo=True, flags=None, leadbook=None, backend=None, fast=False) -> dict`
@@ -437,7 +449,9 @@ Runs a task file over a list of items, updating the list file in place.
437
449
  - `task_file` (str | PathLike): YAML task file (must include `prompt`).
438
450
  - `n` (int | None): limit parallelism to N (default: run all items in parallel).
439
451
  - `cwd` (str | PathLike | None): working directory for the agent session.
440
- - `yolo` (bool): pass `--yolo` when true (defaults to true).
452
+ - `yolo` (bool): use the backend's most permissive unattended mode when true
453
+ (defaults to true). For Codex, `False` uses auto approvals without forcing a
454
+ sandbox policy.
441
455
  - `flags` (str | None): extra CLI flags to pass to the agent backend.
442
456
  - `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
443
457
  - `fast` (bool): enable Codex fast mode (defaults to normal mode).
@@ -459,7 +473,8 @@ Simple result object returned by `foreach()`.
459
473
  `fast=True` / `--fast` also passes `service_tier=fast` and `features.fast_mode=true`.
460
474
  - Cursor backend uses `cursor agent --print --output-format json --trust` and parses the JSON result.
461
475
  - `include_thinking=True` only affects Codex; Cursor returns a single result string.
462
- - Passes `--yolo` by default (Codex uses `--full-auto` when disabled).
476
+ - Uses dangerous no-sandbox automation by default. For Codex, `yolo=False`
477
+ uses auto approvals without forcing a sandbox policy.
463
478
  - Raises `RuntimeError` if the backend exits non-zero or returns no agent message.
464
479
 
465
480
  ## Configuration
@@ -65,6 +65,8 @@ Use `backend="cursor"` (or set `CODEXAPI_BACKEND=cursor`) to switch to the
65
65
  Cursor agent backend.
66
66
  Use `fast=True` in Codex API calls, or `--fast` in the CLI, to opt into Codex
67
67
  fast mode. Normal mode is the default.
68
+ Use `model="..."` and `thinking="..."` in Codex API calls to override the
69
+ backend model and reasoning effort for a run.
68
70
 
69
71
  ## CLI
70
72
 
@@ -140,7 +142,8 @@ Resume a session and print the thread/session id to stderr:
140
142
  codexapi run --thread-id THREAD_ID --print-thread-id "Continue where we left off."
141
143
  ```
142
144
 
143
- Use `--no-yolo` to disable `--yolo` (Codex uses `--full-auto`).
145
+ Use `--no-yolo` to keep unattended operation with Codex auto approvals, without
146
+ forcing a sandbox policy.
144
147
  Use `--include-thinking` to return all agent messages joined together for `codexapi run` (Codex only).
145
148
 
146
149
  Lead mode periodically checks in on a long-running agent session with the
@@ -288,7 +291,8 @@ codexapi ralph --cancel --cwd /path/to/project
288
291
  ```
289
292
 
290
293
  Science mode wraps a short task in a science prompt and runs it through the
291
- Ralph loop. It defaults to `--yolo` and expects progress notes in `SCIENCE.md`.
294
+ Ralph loop. It defaults to dangerous no-sandbox automation and expects progress
295
+ notes in `SCIENCE.md`.
292
296
  Each iteration appends the agent output to `LOGBOOK.md` and the runner extracts
293
297
  any improved figures of merit for optional notifications. You can also set
294
298
  `--max-duration` to stop after the current iteration once a time limit is hit.
@@ -321,33 +325,41 @@ codexapi foreach list.txt task.yaml --retry-all
321
325
 
322
326
  ## API
323
327
 
324
- ### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False) -> str`
328
+ ### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False, model=None, thinking=None) -> str`
325
329
 
326
330
  Runs a single agent turn and returns only the agent's message. Any reasoning
327
331
  items are filtered out.
328
332
 
329
333
  - `prompt` (str): prompt to send to the agent backend.
330
334
  - `cwd` (str | PathLike | None): working directory for the agent session.
331
- - `yolo` (bool): pass `--yolo` when true (defaults to true).
335
+ - `yolo` (bool): use the backend's most permissive unattended mode when true
336
+ (defaults to true). For Codex, `False` uses auto approvals without forcing a
337
+ sandbox policy.
332
338
  - `flags` (str | None): extra CLI flags to pass to the agent backend.
333
339
  - `include_thinking` (bool): when true, return all agent messages joined.
334
340
  - `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
335
341
  - `fast` (bool): enable Codex fast mode (defaults to normal mode).
342
+ - `model` (str | None): backend model override. Codex maps this to `-c model=...`; Cursor maps it to `--model ...`.
343
+ - `thinking` (str | None): Codex reasoning effort override, mapped to `-c model_reasoning_effort=...`.
336
344
 
337
- ### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False)`
345
+ ### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False, model=None, thinking=None)`
338
346
 
339
347
  Creates a stateful session wrapper. Calling the instance sends the prompt into
340
348
  the same conversation and returns only the agent's message.
341
349
 
342
350
  - `__call__(prompt) -> str`: send a prompt to the agent backend and return the message.
343
351
  - `thread_id -> str | None`: expose the underlying session id once created.
344
- - `yolo` (bool): pass `--yolo` when true (defaults to true).
352
+ - `yolo` (bool): use the backend's most permissive unattended mode when true
353
+ (defaults to true). For Codex, `False` uses auto approvals without forcing a
354
+ sandbox policy.
345
355
  - `flags` (str | None): extra CLI flags to pass to the agent backend.
346
356
  - `welfare` (bool): when true, append welfare stop instructions to each prompt
347
357
  and raise `WelfareStop` if the agent outputs `MAKE IT STOP`.
348
358
  - `include_thinking` (bool): when true, return all agent messages joined.
349
359
  - `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
350
360
  - `fast` (bool): enable Codex fast mode (defaults to normal mode).
361
+ - `model` (str | None): backend model override.
362
+ - `thinking` (str | None): Codex reasoning effort override.
351
363
  For Cursor, `thread_id` corresponds to the `session_id` returned by the agent.
352
364
 
353
365
  ### `lead(minutes, prompt, cwd=None, yolo=True, flags=None, leadbook=None, backend=None, fast=False) -> dict`
@@ -422,7 +434,9 @@ Runs a task file over a list of items, updating the list file in place.
422
434
  - `task_file` (str | PathLike): YAML task file (must include `prompt`).
423
435
  - `n` (int | None): limit parallelism to N (default: run all items in parallel).
424
436
  - `cwd` (str | PathLike | None): working directory for the agent session.
425
- - `yolo` (bool): pass `--yolo` when true (defaults to true).
437
+ - `yolo` (bool): use the backend's most permissive unattended mode when true
438
+ (defaults to true). For Codex, `False` uses auto approvals without forcing a
439
+ sandbox policy.
426
440
  - `flags` (str | None): extra CLI flags to pass to the agent backend.
427
441
  - `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
428
442
  - `fast` (bool): enable Codex fast mode (defaults to normal mode).
@@ -444,7 +458,8 @@ Simple result object returned by `foreach()`.
444
458
  `fast=True` / `--fast` also passes `service_tier=fast` and `features.fast_mode=true`.
445
459
  - Cursor backend uses `cursor agent --print --output-format json --trust` and parses the JSON result.
446
460
  - `include_thinking=True` only affects Codex; Cursor returns a single result string.
447
- - Passes `--yolo` by default (Codex uses `--full-auto` when disabled).
461
+ - Uses dangerous no-sandbox automation by default. For Codex, `yolo=False`
462
+ uses auto approvals without forcing a sandbox policy.
448
463
  - Raises `RuntimeError` if the backend exits non-zero or returns no agent message.
449
464
 
450
465
  ## Configuration
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codexapi"
7
- version = "0.12.9"
7
+ version = "0.12.11"
8
8
  description = "Minimal Python API for running the Codex CLI."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -1,6 +1,6 @@
1
1
  """Minimal Python API for running agent CLIs."""
2
2
 
3
- from .agent import Agent, WelfareStop, agent
3
+ from .agent import Agent, WelfareStop, agent, build_agent_flags
4
4
  from .async_agent import AsyncAgent
5
5
  from .foreach import ForeachResult, foreach
6
6
  from .pushover import Pushover
@@ -15,6 +15,7 @@ __all__ = [
15
15
  "AsyncAgent",
16
16
  "ForeachResult",
17
17
  "Pushover",
18
+ "build_agent_flags",
18
19
  "quota_line",
19
20
  "rate_limits",
20
21
  "Ralph",
@@ -29,4 +30,4 @@ __all__ = [
29
30
  "task_result",
30
31
  "lead",
31
32
  ]
32
- __version__ = "0.12.9"
33
+ __version__ = "0.12.11"
@@ -11,6 +11,7 @@ from . import welfare
11
11
  _CODEX_BIN = os.environ.get("CODEX_BIN", "codex")
12
12
  _CURSOR_BIN = os.environ.get("CURSOR_BIN", "cursor")
13
13
  _SUPPORTED_BACKENDS = {"codex", "cursor"}
14
+ _CURSOR_AGENT_BIN = os.path.expanduser("~/.local/bin/cursor-agent")
14
15
 
15
16
 
16
17
  def _resolve_backend(backend):
@@ -33,12 +34,15 @@ def _ensure_backend_available(backend, env=None):
33
34
  env_var = "CODEX_BIN"
34
35
  label = "Codex CLI"
35
36
  else:
36
- command = _CURSOR_BIN
37
+ command = _cursor_bin(env)
37
38
  env_var = "CURSOR_BIN"
38
39
  label = "Cursor agent CLI"
39
40
  merged = _merged_env(env)
40
41
  path_value = None if merged is None else merged.get("PATH")
41
- resolved = shutil.which(command, path=path_value)
42
+ if os.path.isabs(command):
43
+ resolved = command if os.path.exists(command) else None
44
+ else:
45
+ resolved = shutil.which(command, path=path_value)
42
46
  if resolved:
43
47
  return resolved
44
48
  raise RuntimeError(
@@ -55,24 +59,38 @@ def agent(
55
59
  backend=None,
56
60
  env=None,
57
61
  fast=False,
62
+ model=None,
63
+ thinking=None,
58
64
  ):
59
65
  """Run a single agent turn and return only the agent's message.
60
66
 
61
67
  Args:
62
68
  prompt: The user prompt to send to the agent backend.
63
69
  cwd: Optional working directory for the agent session.
64
- yolo: Whether to pass --yolo to the agent backend.
70
+ yolo: Whether to use the backend's most permissive unattended mode.
65
71
  flags: Additional raw CLI flags to pass to the agent backend.
66
72
  include_thinking: When true, return all agent messages joined together.
67
73
  backend: Agent backend to use ("codex" or "cursor").
68
74
  env: Optional environment variables for the backend subprocess.
69
75
  fast: Enable Codex fast mode. Defaults to normal mode.
76
+ model: Optional backend model override.
77
+ thinking: Optional backend reasoning/thinking effort override.
70
78
 
71
79
  Returns:
72
80
  The agent's visible response text with reasoning traces removed.
73
81
  """
74
82
  message, _thread_id, _usage = _run_agent(
75
- prompt, cwd, None, yolo, flags, include_thinking, backend, env, fast
83
+ prompt,
84
+ cwd,
85
+ None,
86
+ yolo,
87
+ flags,
88
+ include_thinking,
89
+ backend,
90
+ env,
91
+ fast,
92
+ model,
93
+ thinking,
76
94
  )
77
95
  return message
78
96
 
@@ -106,12 +124,14 @@ class Agent:
106
124
  backend=None,
107
125
  env=None,
108
126
  fast=False,
127
+ model=None,
128
+ thinking=None,
109
129
  ):
110
130
  """Create a new session wrapper.
111
131
 
112
132
  Args:
113
133
  cwd: Optional working directory for the agent session.
114
- yolo: Whether to pass --yolo to the agent backend.
134
+ yolo: Whether to use the backend's most permissive unattended mode.
115
135
  thread_id: Optional thread/session id to resume from the first call.
116
136
  flags: Additional raw CLI flags to pass to the agent backend.
117
137
  welfare: When true, append welfare stop instructions to each prompt
@@ -120,6 +140,8 @@ class Agent:
120
140
  backend: Agent backend to use ("codex" or "cursor").
121
141
  env: Optional environment variables for the backend subprocess.
122
142
  fast: Enable Codex fast mode. Defaults to normal mode.
143
+ model: Optional backend model override.
144
+ thinking: Optional backend reasoning/thinking effort override.
123
145
  """
124
146
  self.cwd = cwd
125
147
  self._yolo = yolo
@@ -130,6 +152,8 @@ class Agent:
130
152
  self._backend = backend
131
153
  self._env = env
132
154
  self._fast = fast
155
+ self._model = model
156
+ self._thinking = thinking
133
157
  self.last_usage = {}
134
158
 
135
159
  def __call__(self, prompt):
@@ -146,6 +170,8 @@ class Agent:
146
170
  self._backend,
147
171
  self._env,
148
172
  self._fast,
173
+ self._model,
174
+ self._thinking,
149
175
  )
150
176
  if thread_id:
151
177
  self.thread_id = thread_id
@@ -165,29 +191,59 @@ def _run_agent(
165
191
  backend,
166
192
  env,
167
193
  fast=False,
194
+ model=None,
195
+ thinking=None,
168
196
  ):
169
197
  backend = _resolve_backend(backend)
170
198
  _ensure_backend_available(backend, env)
171
199
  if backend == "codex":
172
- return _run_codex(prompt, cwd, thread_id, yolo, flags, include_thinking, env, fast)
173
- return _run_cursor(prompt, cwd, thread_id, yolo, flags, include_thinking, env)
200
+ return _run_codex(
201
+ prompt,
202
+ cwd,
203
+ thread_id,
204
+ yolo,
205
+ flags,
206
+ include_thinking,
207
+ env,
208
+ fast,
209
+ model,
210
+ thinking,
211
+ )
212
+ return _run_cursor(
213
+ prompt,
214
+ cwd,
215
+ thread_id,
216
+ yolo,
217
+ flags,
218
+ include_thinking,
219
+ env,
220
+ model,
221
+ thinking,
222
+ )
174
223
 
175
224
 
176
- def _run_codex(prompt, cwd, thread_id, yolo, flags, include_thinking, env, fast=False):
225
+ def _run_codex(
226
+ prompt,
227
+ cwd,
228
+ thread_id,
229
+ yolo,
230
+ flags,
231
+ include_thinking,
232
+ env,
233
+ fast=False,
234
+ model=None,
235
+ thinking=None,
236
+ ):
177
237
  """Invoke the Codex CLI and return the message plus thread id (if any)."""
178
- command = [
179
- _CODEX_BIN,
238
+ command = [_CODEX_BIN] + _codex_automation_flags(yolo) + [
180
239
  "exec",
181
240
  "--json",
182
241
  "--color",
183
242
  "never",
184
243
  "--skip-git-repo-check",
185
244
  ]
186
- if yolo:
187
- command.append("--yolo")
188
- else:
189
- command.append("--full-auto")
190
245
  command.extend(_codex_fast_config(fast))
246
+ command.extend(_agent_config_flag_parts("codex", model, thinking))
191
247
  if flags:
192
248
  command.extend(shlex.split(flags))
193
249
  if cwd:
@@ -215,6 +271,13 @@ def _run_codex(prompt, cwd, thread_id, yolo, flags, include_thinking, env, fast=
215
271
  return _parse_jsonl(result.stdout, include_thinking)
216
272
 
217
273
 
274
+ def _codex_automation_flags(yolo):
275
+ """Return current Codex CLI flags for unattended operation."""
276
+ if yolo:
277
+ return ["--dangerously-bypass-approvals-and-sandbox"]
278
+ return ["--ask-for-approval", "never"]
279
+
280
+
218
281
  def _codex_fast_config(fast):
219
282
  """Return Codex config flags for normal or fast mode."""
220
283
  if fast:
@@ -227,11 +290,82 @@ def _codex_fast_config(fast):
227
290
  return ["-c", "features.fast_mode=false"]
228
291
 
229
292
 
230
- def _run_cursor(prompt, cwd, thread_id, yolo, flags, include_thinking, env):
293
+ def _cursor_bin(env=None):
294
+ merged = _merged_env(env)
295
+ env_source = merged or os.environ
296
+ override = env_source.get("CURSOR_BIN", "").strip()
297
+ if override:
298
+ return os.path.expanduser(override)
299
+
300
+ path_value = None if merged is None else merged.get("PATH")
301
+ direct = shutil.which("cursor-agent", path=path_value)
302
+ if direct:
303
+ return direct
304
+ if os.path.exists(_CURSOR_AGENT_BIN):
305
+ return _CURSOR_AGENT_BIN
306
+ return _CURSOR_BIN
307
+
308
+
309
+ def _cursor_command_prefix(env=None):
310
+ command = _cursor_bin(env)
311
+ if os.path.basename(command) == "cursor-agent":
312
+ return [command]
313
+ return [command, "agent"]
314
+
315
+
316
+ def build_agent_flags(*, backend=None, model=None, thinking=None, flags=None):
317
+ """Return raw backend flags for a model/thinking configuration.
318
+
319
+ The returned string is suitable for APIs that accept the existing ``flags``
320
+ parameter.
321
+ """
322
+ backend = _resolve_backend(backend)
323
+ parts = _agent_config_flag_parts(backend, model, thinking)
324
+ if flags:
325
+ parts.extend(shlex.split(flags))
326
+ return shlex.join(parts)
327
+
328
+
329
+ def _agent_config_flag_parts(backend, model=None, thinking=None):
330
+ backend = _resolve_backend(backend)
331
+ parts = []
332
+ model = _clean_optional_text(model)
333
+ thinking = _clean_optional_text(thinking)
334
+
335
+ if backend == "codex":
336
+ if model:
337
+ parts.extend(["-c", f"model={model}"])
338
+ if thinking:
339
+ parts.extend(["-c", f"model_reasoning_effort={thinking}"])
340
+ return parts
341
+
342
+ if model:
343
+ parts.extend(["--model", model])
344
+ if thinking:
345
+ raise ValueError("thinking is only supported by the codex backend")
346
+ return parts
347
+
348
+
349
+ def _clean_optional_text(value):
350
+ if value is None:
351
+ return None
352
+ text = str(value).strip()
353
+ return text or None
354
+
355
+
356
+ def _run_cursor(
357
+ prompt,
358
+ cwd,
359
+ thread_id,
360
+ yolo,
361
+ flags,
362
+ include_thinking,
363
+ env,
364
+ model=None,
365
+ thinking=None,
366
+ ):
231
367
  """Invoke the Cursor agent CLI and return the message plus session id (if any)."""
232
- command = [
233
- _CURSOR_BIN,
234
- "agent",
368
+ command = _cursor_command_prefix(env) + [
235
369
  "--trust",
236
370
  ]
237
371
  if cwd:
@@ -240,6 +374,7 @@ def _run_cursor(prompt, cwd, thread_id, yolo, flags, include_thinking, env):
240
374
  command.extend(["--resume", thread_id])
241
375
  if yolo:
242
376
  command.append("--yolo")
377
+ command.extend(_agent_config_flag_parts("cursor", model, thinking))
243
378
  if flags:
244
379
  command.extend(shlex.split(flags))
245
380
  command.extend(["--print", "--output-format", "json"])
@@ -12,8 +12,10 @@ import uuid
12
12
 
13
13
  from .agent import (
14
14
  _CODEX_BIN,
15
- _CURSOR_BIN,
15
+ _agent_config_flag_parts,
16
+ _codex_automation_flags,
16
17
  _codex_fast_config,
18
+ _cursor_command_prefix,
17
19
  _ensure_backend_available,
18
20
  _event_usage,
19
21
  _merged_env,
@@ -88,6 +90,8 @@ class AsyncAgent:
88
90
  env=None,
89
91
  name=None,
90
92
  fast=False,
93
+ model=None,
94
+ thinking=None,
91
95
  ):
92
96
  """Start a backend subprocess and return an async handle immediately."""
93
97
  if not isinstance(prompt, str) or not prompt.strip():
@@ -95,7 +99,7 @@ class AsyncAgent:
95
99
 
96
100
  backend = _resolve_backend(backend)
97
101
  _ensure_backend_available(backend, env)
98
- command = _build_command(backend, cwd, yolo, flags, fast)
102
+ command = _build_command(backend, cwd, yolo, flags, fast, model, thinking)
99
103
  process = subprocess.Popen(
100
104
  command,
101
105
  stdin=subprocess.PIPE,
@@ -376,26 +380,22 @@ class AsyncAgent:
376
380
  return self._rollout_final_output
377
381
 
378
382
 
379
- def _build_command(backend, cwd, yolo, flags, fast=False):
383
+ def _build_command(backend, cwd, yolo, flags, fast=False, model=None, thinking=None):
380
384
  if backend == "codex":
381
- return _build_codex_command(cwd, yolo, flags, fast)
382
- return _build_cursor_command(cwd, yolo, flags)
385
+ return _build_codex_command(cwd, yolo, flags, fast, model, thinking)
386
+ return _build_cursor_command(cwd, yolo, flags, model, thinking)
383
387
 
384
388
 
385
- def _build_codex_command(cwd, yolo, flags, fast=False):
386
- command = [
387
- _CODEX_BIN,
389
+ def _build_codex_command(cwd, yolo, flags, fast=False, model=None, thinking=None):
390
+ command = [_CODEX_BIN] + _codex_automation_flags(yolo) + [
388
391
  "exec",
389
392
  "--json",
390
393
  "--color",
391
394
  "never",
392
395
  "--skip-git-repo-check",
393
396
  ]
394
- if yolo:
395
- command.append("--yolo")
396
- else:
397
- command.append("--full-auto")
398
397
  command.extend(_codex_fast_config(fast))
398
+ command.extend(_agent_config_flag_parts("codex", model, thinking))
399
399
  if flags:
400
400
  command.extend(shlex.split(flags))
401
401
  if cwd:
@@ -404,16 +404,15 @@ def _build_codex_command(cwd, yolo, flags, fast=False):
404
404
  return command
405
405
 
406
406
 
407
- def _build_cursor_command(cwd, yolo, flags):
408
- command = [
409
- _CURSOR_BIN,
410
- "agent",
407
+ def _build_cursor_command(cwd, yolo, flags, model=None, thinking=None):
408
+ command = _cursor_command_prefix() + [
411
409
  "--trust",
412
410
  ]
413
411
  if cwd:
414
412
  command.extend(["--workspace", os.fspath(cwd)])
415
413
  if yolo:
416
414
  command.append("--yolo")
415
+ command.extend(_agent_config_flag_parts("cursor", model, thinking))
417
416
  if flags:
418
417
  command.extend(shlex.split(flags))
419
418
  command.extend(["--print", "--output-format", "json"])
@@ -1439,7 +1439,8 @@ def main(argv=None):
1439
1439
  science_help = (
1440
1440
  "Science mode (science command):\n"
1441
1441
  " Wraps your short task in a science prompt and runs it via the Ralph loop.\n"
1442
- " Default uses --yolo. Use --no-yolo to disable it.\n"
1442
+ " Default uses dangerous no-sandbox automation. "
1443
+ "Use --no-yolo for auto approvals without forcing sandbox policy.\n"
1443
1444
  " Optional --max-duration stops before starting the next iteration once\n"
1444
1445
  " the duration limit is reached (e.g. 90m, 2h, 45s; default unit is minutes).\n"
1445
1446
  )
@@ -1479,7 +1480,7 @@ def main(argv=None):
1479
1480
  "--no-yolo",
1480
1481
  action="store_false",
1481
1482
  dest="yolo",
1482
- help="Disable --yolo (Codex uses --full-auto).",
1483
+ help="Use auto approvals instead of dangerous no-sandbox automation.",
1483
1484
  )
1484
1485
  run_parser.add_argument(
1485
1486
  "--flags",
@@ -1535,7 +1536,7 @@ def main(argv=None):
1535
1536
  "--no-yolo",
1536
1537
  action="store_false",
1537
1538
  dest="yolo",
1538
- help="Disable --yolo (Codex uses --full-auto).",
1539
+ help="Use auto approvals instead of dangerous no-sandbox automation.",
1539
1540
  )
1540
1541
  lead_parser.add_argument(
1541
1542
  "--flags",
@@ -1604,7 +1605,7 @@ def main(argv=None):
1604
1605
  "--no-yolo",
1605
1606
  action="store_false",
1606
1607
  dest="yolo",
1607
- help="Disable --yolo (Codex uses --full-auto).",
1608
+ help="Use auto approvals instead of dangerous no-sandbox automation.",
1608
1609
  )
1609
1610
  agent_start.add_argument(
1610
1611
  "--flags",
@@ -1837,7 +1838,7 @@ def main(argv=None):
1837
1838
  "--no-yolo",
1838
1839
  action="store_false",
1839
1840
  dest="yolo",
1840
- help="Disable --yolo (Codex uses --full-auto).",
1841
+ help="Use auto approvals instead of dangerous no-sandbox automation.",
1841
1842
  )
1842
1843
  task_parser.add_argument(
1843
1844
  "--flags",
@@ -1911,7 +1912,7 @@ def main(argv=None):
1911
1912
  "--no-yolo",
1912
1913
  action="store_false",
1913
1914
  dest="yolo",
1914
- help="Disable --yolo (Codex uses --full-auto).",
1915
+ help="Use auto approvals instead of dangerous no-sandbox automation.",
1915
1916
  )
1916
1917
  ralph_parser.add_argument(
1917
1918
  "--flags",
@@ -1982,7 +1983,7 @@ def main(argv=None):
1982
1983
  "--no-yolo",
1983
1984
  action="store_false",
1984
1985
  dest="yolo",
1985
- help="Disable --yolo (Codex uses --full-auto).",
1986
+ help="Use auto approvals instead of dangerous no-sandbox automation.",
1986
1987
  )
1987
1988
  science_parser.add_argument(
1988
1989
  "--flags",
@@ -2033,7 +2034,7 @@ def main(argv=None):
2033
2034
  "--no-yolo",
2034
2035
  action="store_false",
2035
2036
  dest="yolo",
2036
- help="Disable --yolo (Codex uses --full-auto).",
2037
+ help="Use auto approvals instead of dangerous no-sandbox automation.",
2037
2038
  )
2038
2039
  foreach_parser.add_argument(
2039
2040
  "--flags",
@@ -91,7 +91,7 @@ def lead(
91
91
  minutes: Check-in interval in whole minutes (>= 0).
92
92
  prompt: The original instruction prompt.
93
93
  cwd: Optional working directory for the agent session.
94
- yolo: Whether to pass --yolo to the agent backend.
94
+ yolo: Whether to use the backend's most permissive unattended mode.
95
95
  flags: Additional raw CLI flags to pass to the agent backend.
96
96
  leadbook: Optional path to the leadbook file. Set to False to disable.
97
97
  backend: Agent backend to use ("codex" or "cursor").
@@ -275,7 +275,7 @@ def task(
275
275
  a string check prompt. The string "None" skips verification.
276
276
  max_iterations: Maximum number of task iterations (0 means unlimited).
277
277
  cwd: Optional working directory for the agent session.
278
- yolo: Whether to pass --yolo to the agent backend.
278
+ yolo: Whether to use the backend's most permissive unattended mode.
279
279
  flags: Additional raw CLI flags to pass to the agent backend.
280
280
  progress: Whether to show a tqdm progress bar with status updates.
281
281
  set_up: Optional setup prompt to run before the task.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: codexapi
3
- Version: 0.12.9
3
+ Version: 0.12.11
4
4
  Summary: Minimal Python API for running the Codex CLI.
5
5
  License: MIT
6
6
  Keywords: codex,agent,cli,openai
@@ -80,6 +80,8 @@ Use `backend="cursor"` (or set `CODEXAPI_BACKEND=cursor`) to switch to the
80
80
  Cursor agent backend.
81
81
  Use `fast=True` in Codex API calls, or `--fast` in the CLI, to opt into Codex
82
82
  fast mode. Normal mode is the default.
83
+ Use `model="..."` and `thinking="..."` in Codex API calls to override the
84
+ backend model and reasoning effort for a run.
83
85
 
84
86
  ## CLI
85
87
 
@@ -155,7 +157,8 @@ Resume a session and print the thread/session id to stderr:
155
157
  codexapi run --thread-id THREAD_ID --print-thread-id "Continue where we left off."
156
158
  ```
157
159
 
158
- Use `--no-yolo` to disable `--yolo` (Codex uses `--full-auto`).
160
+ Use `--no-yolo` to keep unattended operation with Codex auto approvals, without
161
+ forcing a sandbox policy.
159
162
  Use `--include-thinking` to return all agent messages joined together for `codexapi run` (Codex only).
160
163
 
161
164
  Lead mode periodically checks in on a long-running agent session with the
@@ -303,7 +306,8 @@ codexapi ralph --cancel --cwd /path/to/project
303
306
  ```
304
307
 
305
308
  Science mode wraps a short task in a science prompt and runs it through the
306
- Ralph loop. It defaults to `--yolo` and expects progress notes in `SCIENCE.md`.
309
+ Ralph loop. It defaults to dangerous no-sandbox automation and expects progress
310
+ notes in `SCIENCE.md`.
307
311
  Each iteration appends the agent output to `LOGBOOK.md` and the runner extracts
308
312
  any improved figures of merit for optional notifications. You can also set
309
313
  `--max-duration` to stop after the current iteration once a time limit is hit.
@@ -336,33 +340,41 @@ codexapi foreach list.txt task.yaml --retry-all
336
340
 
337
341
  ## API
338
342
 
339
- ### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False) -> str`
343
+ ### `agent(prompt, cwd=None, yolo=True, flags=None, include_thinking=False, backend=None, fast=False, model=None, thinking=None) -> str`
340
344
 
341
345
  Runs a single agent turn and returns only the agent's message. Any reasoning
342
346
  items are filtered out.
343
347
 
344
348
  - `prompt` (str): prompt to send to the agent backend.
345
349
  - `cwd` (str | PathLike | None): working directory for the agent session.
346
- - `yolo` (bool): pass `--yolo` when true (defaults to true).
350
+ - `yolo` (bool): use the backend's most permissive unattended mode when true
351
+ (defaults to true). For Codex, `False` uses auto approvals without forcing a
352
+ sandbox policy.
347
353
  - `flags` (str | None): extra CLI flags to pass to the agent backend.
348
354
  - `include_thinking` (bool): when true, return all agent messages joined.
349
355
  - `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
350
356
  - `fast` (bool): enable Codex fast mode (defaults to normal mode).
357
+ - `model` (str | None): backend model override. Codex maps this to `-c model=...`; Cursor maps it to `--model ...`.
358
+ - `thinking` (str | None): Codex reasoning effort override, mapped to `-c model_reasoning_effort=...`.
351
359
 
352
- ### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False)`
360
+ ### `Agent(cwd=None, yolo=True, thread_id=None, flags=None, welfare=False, include_thinking=False, backend=None, fast=False, model=None, thinking=None)`
353
361
 
354
362
  Creates a stateful session wrapper. Calling the instance sends the prompt into
355
363
  the same conversation and returns only the agent's message.
356
364
 
357
365
  - `__call__(prompt) -> str`: send a prompt to the agent backend and return the message.
358
366
  - `thread_id -> str | None`: expose the underlying session id once created.
359
- - `yolo` (bool): pass `--yolo` when true (defaults to true).
367
+ - `yolo` (bool): use the backend's most permissive unattended mode when true
368
+ (defaults to true). For Codex, `False` uses auto approvals without forcing a
369
+ sandbox policy.
360
370
  - `flags` (str | None): extra CLI flags to pass to the agent backend.
361
371
  - `welfare` (bool): when true, append welfare stop instructions to each prompt
362
372
  and raise `WelfareStop` if the agent outputs `MAKE IT STOP`.
363
373
  - `include_thinking` (bool): when true, return all agent messages joined.
364
374
  - `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
365
375
  - `fast` (bool): enable Codex fast mode (defaults to normal mode).
376
+ - `model` (str | None): backend model override.
377
+ - `thinking` (str | None): Codex reasoning effort override.
366
378
  For Cursor, `thread_id` corresponds to the `session_id` returned by the agent.
367
379
 
368
380
  ### `lead(minutes, prompt, cwd=None, yolo=True, flags=None, leadbook=None, backend=None, fast=False) -> dict`
@@ -437,7 +449,9 @@ Runs a task file over a list of items, updating the list file in place.
437
449
  - `task_file` (str | PathLike): YAML task file (must include `prompt`).
438
450
  - `n` (int | None): limit parallelism to N (default: run all items in parallel).
439
451
  - `cwd` (str | PathLike | None): working directory for the agent session.
440
- - `yolo` (bool): pass `--yolo` when true (defaults to true).
452
+ - `yolo` (bool): use the backend's most permissive unattended mode when true
453
+ (defaults to true). For Codex, `False` uses auto approvals without forcing a
454
+ sandbox policy.
441
455
  - `flags` (str | None): extra CLI flags to pass to the agent backend.
442
456
  - `backend` (str | None): `codex` or `cursor` (defaults to `CODEXAPI_BACKEND` or `codex`).
443
457
  - `fast` (bool): enable Codex fast mode (defaults to normal mode).
@@ -459,7 +473,8 @@ Simple result object returned by `foreach()`.
459
473
  `fast=True` / `--fast` also passes `service_tier=fast` and `features.fast_mode=true`.
460
474
  - Cursor backend uses `cursor agent --print --output-format json --trust` and parses the JSON result.
461
475
  - `include_thinking=True` only affects Codex; Cursor returns a single result string.
462
- - Passes `--yolo` by default (Codex uses `--full-auto` when disabled).
476
+ - Uses dangerous no-sandbox automation by default. For Codex, `yolo=False`
477
+ uses auto approvals without forcing a sandbox policy.
463
478
  - Raises `RuntimeError` if the backend exits non-zero or returns no agent message.
464
479
 
465
480
  ## Configuration
@@ -2,11 +2,12 @@ import json
2
2
  import sys
3
3
  import unittest
4
4
  from pathlib import Path
5
+ from unittest.mock import patch
5
6
 
6
7
  sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
7
8
 
8
- from codexapi.agent import _codex_fast_config, _parse_jsonl
9
- from codexapi.async_agent import _build_codex_command
9
+ from codexapi.agent import _codex_fast_config, _parse_jsonl, build_agent_flags
10
+ from codexapi.async_agent import _build_codex_command, _build_cursor_command
10
11
 
11
12
 
12
13
  class AgentBackendTests(unittest.TestCase):
@@ -29,11 +30,59 @@ class AgentBackendTests(unittest.TestCase):
29
30
  self.assertIn("features.fast_mode=false", command)
30
31
  self.assertNotIn("service_tier=fast", command)
31
32
 
33
+ def test_async_codex_command_uses_documented_yolo_flags(self):
34
+ command = _build_codex_command(None, True, None)
35
+ exec_index = command.index("exec")
36
+ self.assertIn("--dangerously-bypass-approvals-and-sandbox", command)
37
+ self.assertLess(
38
+ command.index("--dangerously-bypass-approvals-and-sandbox"),
39
+ exec_index,
40
+ )
41
+ self.assertGreater(command.index("--json"), exec_index)
42
+ self.assertNotIn("--yolo", command)
43
+
44
+ def test_async_codex_command_no_yolo_uses_auto_approval_mode(self):
45
+ command = _build_codex_command(None, False, None)
46
+ exec_index = command.index("exec")
47
+ self.assertLess(command.index("--ask-for-approval"), exec_index)
48
+ self.assertIn("never", command)
49
+ self.assertNotIn("--sandbox", command)
50
+ self.assertNotIn("--full-auto", command)
51
+
32
52
  def test_async_codex_command_can_enable_fast_mode(self):
33
53
  command = _build_codex_command(None, True, None, fast=True)
34
54
  self.assertIn("service_tier=fast", command)
35
55
  self.assertIn("features.fast_mode=true", command)
36
56
 
57
+ def test_build_agent_flags_maps_codex_model_and_thinking_to_config(self):
58
+ self.assertEqual(
59
+ build_agent_flags(backend="codex", model="gpt-5.5", thinking="xhigh"),
60
+ "-c model=gpt-5.5 -c model_reasoning_effort=xhigh",
61
+ )
62
+
63
+ def test_build_agent_flags_maps_cursor_model_to_model_flag(self):
64
+ self.assertEqual(
65
+ build_agent_flags(backend="cursor", model="claude-4"),
66
+ "--model claude-4",
67
+ )
68
+
69
+ def test_build_agent_flags_rejects_cursor_thinking(self):
70
+ with self.assertRaises(ValueError):
71
+ build_agent_flags(backend="cursor", thinking="high")
72
+
73
+ def test_async_codex_command_can_set_model_and_thinking(self):
74
+ command = _build_codex_command(None, True, None, model="gpt-5.5", thinking="high")
75
+ self.assertIn("model=gpt-5.5", command)
76
+ self.assertIn("model_reasoning_effort=high", command)
77
+
78
+ def test_async_cursor_command_can_use_direct_cursor_agent(self):
79
+ with patch("codexapi.async_agent._cursor_command_prefix", return_value=["/tmp/cursor-agent"]):
80
+ command = _build_cursor_command("/tmp/work", True, None, model="composer-2")
81
+ self.assertEqual(command[0], "/tmp/cursor-agent")
82
+ self.assertNotEqual(command[1], "agent")
83
+ self.assertIn("--model", command)
84
+ self.assertIn("composer-2", command)
85
+
37
86
  def test_parse_jsonl_extracts_last_token_usage(self):
38
87
  output = "\n".join(
39
88
  [
File without changes
File without changes