codexapi 0.6.3__tar.gz → 0.6.4__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.
- {codexapi-0.6.3/src/codexapi.egg-info → codexapi-0.6.4}/PKG-INFO +11 -5
- {codexapi-0.6.3 → codexapi-0.6.4}/README.md +10 -4
- {codexapi-0.6.3 → codexapi-0.6.4}/pyproject.toml +1 -1
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/__init__.py +1 -1
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/cli.py +2 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/pushover.py +1 -1
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/watch.py +102 -3
- {codexapi-0.6.3 → codexapi-0.6.4/src/codexapi.egg-info}/PKG-INFO +11 -5
- {codexapi-0.6.3 → codexapi-0.6.4}/LICENSE +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/setup.cfg +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/__main__.py +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/agent.py +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/foreach.py +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/gh_integration.py +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/ralph.py +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/rate_limits.py +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/science.py +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/task.py +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/taskfile.py +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi/welfare.py +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi.egg-info/SOURCES.txt +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi.egg-info/dependency_links.txt +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi.egg-info/entry_points.txt +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi.egg-info/requires.txt +0 -0
- {codexapi-0.6.3 → codexapi-0.6.4}/src/codexapi.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codexapi
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
4
4
|
Summary: Minimal Python API for running the Codex CLI.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: codex,agent,cli,openai
|
|
@@ -128,7 +128,11 @@ Use `--no-yolo` to run Codex with `--full-auto` instead.
|
|
|
128
128
|
|
|
129
129
|
Watch mode periodically ticks a long-running agent session with the current time
|
|
130
130
|
and prints JSON status updates. The agent controls the loop by setting
|
|
131
|
-
`continue` to true/false in its JSON response.
|
|
131
|
+
`continue` to true/false in its JSON response. Each tick expects JSON keys:
|
|
132
|
+
`status` (one line), `continue` (bool), and optional `comments` (string). If the
|
|
133
|
+
JSON is invalid, watch asks the agent once to retry before stopping with an
|
|
134
|
+
error. When `~/.pushover` is configured, watch sends a notification when it
|
|
135
|
+
stops.
|
|
132
136
|
|
|
133
137
|
```bash
|
|
134
138
|
codexapi watch 5 "Run the benchmark and wait for results."
|
|
@@ -162,7 +166,8 @@ Optional Pushover notifications: create `~/.pushover` with two non-empty lines.
|
|
|
162
166
|
Line 1 is your user or group key, line 2 is the app API token. When this file
|
|
163
167
|
exists, Science will send a notification whenever it detects a new best result,
|
|
164
168
|
including the metric values and percent improvement. Task runs will also send a
|
|
165
|
-
✅/❌ notification with the task summary.
|
|
169
|
+
✅/❌ notification with the task summary. Watch runs send a notification when the
|
|
170
|
+
loop stops.
|
|
166
171
|
|
|
167
172
|
Run a task file across a list file:
|
|
168
173
|
|
|
@@ -201,8 +206,9 @@ the same conversation and returns only the agent's message.
|
|
|
201
206
|
|
|
202
207
|
Runs a long-lived agent session and periodically "ticks" it with the current
|
|
203
208
|
local time and a reminder of `prompt`. Each tick expects JSON with keys:
|
|
204
|
-
`status` (one line), `continue` (bool), and `comments` (string).
|
|
205
|
-
|
|
209
|
+
`status` (one line), `continue` (bool), and optional `comments` (string). If the
|
|
210
|
+
JSON is invalid, watch asks the agent once to retry. The loop stops when
|
|
211
|
+
`continue` is false and sends a Pushover notification (when configured).
|
|
206
212
|
|
|
207
213
|
### `task(prompt, check=None, max_iterations=10, cwd=None, yolo=True, flags=None, progress=False, set_up=None, tear_down=None, on_success=None, on_failure=None) -> str`
|
|
208
214
|
|
|
@@ -113,7 +113,11 @@ Use `--no-yolo` to run Codex with `--full-auto` instead.
|
|
|
113
113
|
|
|
114
114
|
Watch mode periodically ticks a long-running agent session with the current time
|
|
115
115
|
and prints JSON status updates. The agent controls the loop by setting
|
|
116
|
-
`continue` to true/false in its JSON response.
|
|
116
|
+
`continue` to true/false in its JSON response. Each tick expects JSON keys:
|
|
117
|
+
`status` (one line), `continue` (bool), and optional `comments` (string). If the
|
|
118
|
+
JSON is invalid, watch asks the agent once to retry before stopping with an
|
|
119
|
+
error. When `~/.pushover` is configured, watch sends a notification when it
|
|
120
|
+
stops.
|
|
117
121
|
|
|
118
122
|
```bash
|
|
119
123
|
codexapi watch 5 "Run the benchmark and wait for results."
|
|
@@ -147,7 +151,8 @@ Optional Pushover notifications: create `~/.pushover` with two non-empty lines.
|
|
|
147
151
|
Line 1 is your user or group key, line 2 is the app API token. When this file
|
|
148
152
|
exists, Science will send a notification whenever it detects a new best result,
|
|
149
153
|
including the metric values and percent improvement. Task runs will also send a
|
|
150
|
-
✅/❌ notification with the task summary.
|
|
154
|
+
✅/❌ notification with the task summary. Watch runs send a notification when the
|
|
155
|
+
loop stops.
|
|
151
156
|
|
|
152
157
|
Run a task file across a list file:
|
|
153
158
|
|
|
@@ -186,8 +191,9 @@ the same conversation and returns only the agent's message.
|
|
|
186
191
|
|
|
187
192
|
Runs a long-lived agent session and periodically "ticks" it with the current
|
|
188
193
|
local time and a reminder of `prompt`. Each tick expects JSON with keys:
|
|
189
|
-
`status` (one line), `continue` (bool), and `comments` (string).
|
|
190
|
-
|
|
194
|
+
`status` (one line), `continue` (bool), and optional `comments` (string). If the
|
|
195
|
+
JSON is invalid, watch asks the agent once to retry. The loop stops when
|
|
196
|
+
`continue` is false and sends a Pushover notification (when configured).
|
|
191
197
|
|
|
192
198
|
### `task(prompt, check=None, max_iterations=10, cwd=None, yolo=True, flags=None, progress=False, set_up=None, tear_down=None, on_success=None, on_failure=None) -> str`
|
|
193
199
|
|
|
@@ -1543,6 +1543,8 @@ def main(argv=None):
|
|
|
1543
1543
|
watch(args.minutes, prompt, args.cwd, args.yolo, args.flags)
|
|
1544
1544
|
except KeyboardInterrupt:
|
|
1545
1545
|
raise SystemExit(130)
|
|
1546
|
+
except Exception as exc:
|
|
1547
|
+
raise SystemExit(str(exc) or "watch failed") from None
|
|
1546
1548
|
return
|
|
1547
1549
|
if args.command == "task":
|
|
1548
1550
|
if args.project:
|
|
@@ -15,7 +15,7 @@ _PUSHOVER_URL = "https://api.pushover.net/1/messages.json"
|
|
|
15
15
|
_MAX_MESSAGE = 1024
|
|
16
16
|
|
|
17
17
|
_STARTUP_MESSAGE = (
|
|
18
|
-
"Pushover user and app keys read, notifications for task
|
|
18
|
+
"Pushover user and app keys read, notifications for task/science/watch enabled."
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
|
|
@@ -6,17 +6,19 @@ small JSON status payload so the loop can decide whether to continue.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
|
+
import sys
|
|
9
10
|
import time
|
|
10
11
|
from datetime import datetime
|
|
11
12
|
|
|
12
13
|
from .agent import Agent
|
|
14
|
+
from .pushover import Pushover
|
|
13
15
|
|
|
14
16
|
_JSON_INSTRUCTIONS = (
|
|
15
17
|
"Respond with JSON only (no markdown/backticks/extra text).\n"
|
|
16
18
|
"Return a single JSON object with keys:\n"
|
|
17
19
|
" status: string (one line)\n"
|
|
18
20
|
" continue: boolean\n"
|
|
19
|
-
" comments: string\n"
|
|
21
|
+
" comments: string (optional)\n"
|
|
20
22
|
"To stop this watch loop, set continue to false."
|
|
21
23
|
)
|
|
22
24
|
|
|
@@ -43,6 +45,9 @@ def watch(minutes, prompt, cwd=None, yolo=True, flags=None):
|
|
|
43
45
|
|
|
44
46
|
interval = minutes * 60
|
|
45
47
|
session = Agent(cwd, yolo, None, flags)
|
|
48
|
+
pushover = Pushover()
|
|
49
|
+
pushover.ensure_ready()
|
|
50
|
+
title = _format_title(prompt)
|
|
46
51
|
|
|
47
52
|
last_sent = None
|
|
48
53
|
last_result = None
|
|
@@ -57,11 +62,34 @@ def watch(minutes, prompt, cwd=None, yolo=True, flags=None):
|
|
|
57
62
|
now = datetime.now().astimezone().isoformat(timespec="seconds")
|
|
58
63
|
message = _build_tick_prompt(prompt, now, elapsed, tick)
|
|
59
64
|
output = session(message)
|
|
60
|
-
|
|
65
|
+
try:
|
|
66
|
+
result = _parse_status(output)
|
|
67
|
+
except ValueError as exc:
|
|
68
|
+
print(
|
|
69
|
+
f"[watch {tick} {now}] Invalid JSON from agent, requesting retry: {exc}",
|
|
70
|
+
file=sys.stderr,
|
|
71
|
+
)
|
|
72
|
+
retry_prompt = _json_retry_prompt(prompt, tick, str(exc), output)
|
|
73
|
+
retry_output = session(retry_prompt)
|
|
74
|
+
try:
|
|
75
|
+
result = _parse_status(retry_output)
|
|
76
|
+
except ValueError as exc2:
|
|
77
|
+
details = _format_json_double_failure(
|
|
78
|
+
str(exc),
|
|
79
|
+
output,
|
|
80
|
+
str(exc2),
|
|
81
|
+
retry_output,
|
|
82
|
+
)
|
|
83
|
+
pushover.send(title, f"Watch stopped (invalid JSON).\n{details}")
|
|
84
|
+
raise RuntimeError(
|
|
85
|
+
"Agent was unable to provide valid JSON output after retry.\n"
|
|
86
|
+
+ details
|
|
87
|
+
) from None
|
|
61
88
|
last_result = result
|
|
62
89
|
_print_status(now, elapsed, tick, result)
|
|
63
90
|
|
|
64
91
|
if not result["continue"]:
|
|
92
|
+
pushover.send(title, _format_stop_message(tick, now, result))
|
|
65
93
|
return last_result
|
|
66
94
|
|
|
67
95
|
next_tick = sent_at + interval
|
|
@@ -131,6 +159,78 @@ def _parse_status(output):
|
|
|
131
159
|
}
|
|
132
160
|
|
|
133
161
|
|
|
162
|
+
def _json_retry_prompt(prompt, tick, error, output):
|
|
163
|
+
snippet = _snippet(output, 600)
|
|
164
|
+
lines = [
|
|
165
|
+
f"Your last message (tick {tick}) was not valid JSON.",
|
|
166
|
+
f"Error: {error}",
|
|
167
|
+
"",
|
|
168
|
+
"Here is your previous output (truncated):",
|
|
169
|
+
snippet,
|
|
170
|
+
"",
|
|
171
|
+
"Please try again and respond with JSON only.",
|
|
172
|
+
"",
|
|
173
|
+
"A reminder: your instructions are:",
|
|
174
|
+
prompt.strip(),
|
|
175
|
+
"",
|
|
176
|
+
_JSON_INSTRUCTIONS,
|
|
177
|
+
]
|
|
178
|
+
return "\n".join(lines).strip()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _format_title(prompt):
|
|
182
|
+
text = _single_line(prompt).strip() or "codexapi watch"
|
|
183
|
+
if len(text) > 60:
|
|
184
|
+
text = text[:57] + "..."
|
|
185
|
+
return f"Watch: {text}"
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _format_stop_message(tick, now, result):
|
|
189
|
+
status = _single_line(result.get("status") or "").strip()
|
|
190
|
+
header = f"Watch stopped at tick {tick} ({now})."
|
|
191
|
+
if status:
|
|
192
|
+
header = f"{header} {status}"
|
|
193
|
+
comments = (result.get("comments") or "").strip()
|
|
194
|
+
if comments:
|
|
195
|
+
return f"{header}\n{comments}"
|
|
196
|
+
return header
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _format_json_failure(error, output):
|
|
200
|
+
snippet = _snippet(output, 600)
|
|
201
|
+
return "\n".join(
|
|
202
|
+
[
|
|
203
|
+
f"Error: {error}",
|
|
204
|
+
"",
|
|
205
|
+
"Last output (truncated):",
|
|
206
|
+
snippet,
|
|
207
|
+
]
|
|
208
|
+
).strip()
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _format_json_double_failure(error_1, output_1, error_2, output_2):
|
|
212
|
+
first = _format_json_failure(error_1, output_1)
|
|
213
|
+
second = _format_json_failure(error_2, output_2)
|
|
214
|
+
return "\n".join(
|
|
215
|
+
[
|
|
216
|
+
"First attempt:",
|
|
217
|
+
first,
|
|
218
|
+
"",
|
|
219
|
+
"Second attempt:",
|
|
220
|
+
second,
|
|
221
|
+
]
|
|
222
|
+
).strip()
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _snippet(text, limit):
|
|
226
|
+
text = str(text or "").strip()
|
|
227
|
+
if not text:
|
|
228
|
+
return "(empty)"
|
|
229
|
+
if len(text) <= limit:
|
|
230
|
+
return text
|
|
231
|
+
return text[:limit].rstrip() + "..."
|
|
232
|
+
|
|
233
|
+
|
|
134
234
|
def _maybe_strip_code_fence(text):
|
|
135
235
|
if not text.startswith("```"):
|
|
136
236
|
return text
|
|
@@ -177,4 +277,3 @@ def _print_status(now, elapsed, tick, result):
|
|
|
177
277
|
comments = result.get("comments") or ""
|
|
178
278
|
if comments.strip():
|
|
179
279
|
print(comments.rstrip())
|
|
180
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: codexapi
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
4
4
|
Summary: Minimal Python API for running the Codex CLI.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: codex,agent,cli,openai
|
|
@@ -128,7 +128,11 @@ Use `--no-yolo` to run Codex with `--full-auto` instead.
|
|
|
128
128
|
|
|
129
129
|
Watch mode periodically ticks a long-running agent session with the current time
|
|
130
130
|
and prints JSON status updates. The agent controls the loop by setting
|
|
131
|
-
`continue` to true/false in its JSON response.
|
|
131
|
+
`continue` to true/false in its JSON response. Each tick expects JSON keys:
|
|
132
|
+
`status` (one line), `continue` (bool), and optional `comments` (string). If the
|
|
133
|
+
JSON is invalid, watch asks the agent once to retry before stopping with an
|
|
134
|
+
error. When `~/.pushover` is configured, watch sends a notification when it
|
|
135
|
+
stops.
|
|
132
136
|
|
|
133
137
|
```bash
|
|
134
138
|
codexapi watch 5 "Run the benchmark and wait for results."
|
|
@@ -162,7 +166,8 @@ Optional Pushover notifications: create `~/.pushover` with two non-empty lines.
|
|
|
162
166
|
Line 1 is your user or group key, line 2 is the app API token. When this file
|
|
163
167
|
exists, Science will send a notification whenever it detects a new best result,
|
|
164
168
|
including the metric values and percent improvement. Task runs will also send a
|
|
165
|
-
✅/❌ notification with the task summary.
|
|
169
|
+
✅/❌ notification with the task summary. Watch runs send a notification when the
|
|
170
|
+
loop stops.
|
|
166
171
|
|
|
167
172
|
Run a task file across a list file:
|
|
168
173
|
|
|
@@ -201,8 +206,9 @@ the same conversation and returns only the agent's message.
|
|
|
201
206
|
|
|
202
207
|
Runs a long-lived agent session and periodically "ticks" it with the current
|
|
203
208
|
local time and a reminder of `prompt`. Each tick expects JSON with keys:
|
|
204
|
-
`status` (one line), `continue` (bool), and `comments` (string).
|
|
205
|
-
|
|
209
|
+
`status` (one line), `continue` (bool), and optional `comments` (string). If the
|
|
210
|
+
JSON is invalid, watch asks the agent once to retry. The loop stops when
|
|
211
|
+
`continue` is false and sends a Pushover notification (when configured).
|
|
206
212
|
|
|
207
213
|
### `task(prompt, check=None, max_iterations=10, cwd=None, yolo=True, flags=None, progress=False, set_up=None, tear_down=None, on_success=None, on_failure=None) -> str`
|
|
208
214
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|