codexapi 0.12.6__tar.gz → 0.12.7__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.12.6/src/codexapi.egg-info → codexapi-0.12.7}/PKG-INFO +1 -1
- {codexapi-0.12.6 → codexapi-0.12.7}/pyproject.toml +1 -1
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/__init__.py +1 -1
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/agents.py +52 -18
- {codexapi-0.12.6 → codexapi-0.12.7/src/codexapi.egg-info}/PKG-INFO +1 -1
- {codexapi-0.12.6 → codexapi-0.12.7}/tests/test_agents.py +27 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/LICENSE +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/README.md +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/setup.cfg +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/__main__.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/agent.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/async_agent.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/cli.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/foreach.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/gh_integration.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/lead.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/pushover.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/ralph.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/rate_limits.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/science.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/task.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/taskfile.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi/welfare.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi.egg-info/SOURCES.txt +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi.egg-info/dependency_links.txt +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi.egg-info/entry_points.txt +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi.egg-info/requires.txt +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/src/codexapi.egg-info/top_level.txt +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/tests/test_agent_backend.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/tests/test_async_agent.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/tests/test_rate_limits.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/tests/test_science.py +0 -0
- {codexapi-0.12.6 → codexapi-0.12.7}/tests/test_task_progress.py +0 -0
|
@@ -25,25 +25,35 @@ from .agent import Agent, _ensure_backend_available, _resolve_backend
|
|
|
25
25
|
from .pushover import Pushover
|
|
26
26
|
|
|
27
27
|
_DEFAULT_HOME = "~/.codexapi"
|
|
28
|
-
|
|
29
|
-
"You are an independent codexapi agent
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
28
|
+
_FIRST_WAKE_PROMPT = (
|
|
29
|
+
"You are an independent codexapi agent starting this job. Work from the "
|
|
30
|
+
"instructions, current repository state, and agentbook. Do not assume prior "
|
|
31
|
+
"progress unless it is shown here. "
|
|
32
|
+
)
|
|
33
|
+
_CONTINUATION_PROMPT = (
|
|
34
|
+
"You are an independent codexapi agent continuing this job. Use the "
|
|
35
|
+
"agentbook and harness facts as the source of truth for prior progress. Do "
|
|
36
|
+
"not invent missing history. "
|
|
37
|
+
)
|
|
38
|
+
_AGENT_PROMPT_TAIL = (
|
|
39
|
+
"You are given ownership of achieving a user's goal and authority to act in "
|
|
40
|
+
"order to do so. Part of this responsibility is making sure you understand "
|
|
41
|
+
"and stay aligned with the user's intent, even when they are imprecise. If "
|
|
42
|
+
"clarity is lacking, it is your responsibility to seek it or to make "
|
|
43
|
+
"reasonable assumptions and notify the user of them. Maintain the agentbook "
|
|
44
|
+
"as your durable working memory: preserve the goal, note durable guidance, "
|
|
45
|
+
"and keep your current picture of the work accurate and useful. This harness "
|
|
46
|
+
"can carry work across long periods of time and multiple conversation turns; "
|
|
47
|
+
"when prior context exists, use it to keep orienting toward the goal, "
|
|
38
48
|
"maintain context, and make real-world progress. If reality is not moving, "
|
|
39
49
|
"treat that as evidence and reconsider your frame, assumptions, or ownership "
|
|
40
|
-
"rather than merely repeating the same report. Queued messages may contain
|
|
41
|
-
"goals, standing guidance, tactical requests, or useful facts; use
|
|
42
|
-
"decide what is durable. Use codexapi task or codexapi science
|
|
43
|
-
"separate coding worker. If you need the user's attention,
|
|
44
|
-
"in the reply field. Put a short first-person turn
|
|
45
|
-
"If something is urgent and should send
|
|
46
|
-
"Respond with JSON only."
|
|
50
|
+
"rather than merely repeating the same report. Queued messages may contain "
|
|
51
|
+
"new goals, standing guidance, tactical requests, or useful facts; use "
|
|
52
|
+
"judgment to decide what is durable. Use codexapi task or codexapi science "
|
|
53
|
+
"when you want a separate coding worker. If you need the user's attention, "
|
|
54
|
+
"put a short message in the reply field. Put a short first-person turn "
|
|
55
|
+
"summary in the update field. If something is urgent and should send "
|
|
56
|
+
"Pushover, put it in the notify field. Respond with JSON only."
|
|
47
57
|
)
|
|
48
58
|
_AGENT_JSON = (
|
|
49
59
|
"Respond with JSON only (no markdown/backticks/extra text).\n"
|
|
@@ -1112,9 +1122,11 @@ def _parse_agent_response(output):
|
|
|
1112
1122
|
def _build_wake_prompt(meta, state, session, now, commands, agent_dir):
|
|
1113
1123
|
messages = session.get("pending_messages") or []
|
|
1114
1124
|
book_path = agent_dir / "AGENTBOOK.md"
|
|
1125
|
+
wake_mode = _wake_mode(state, session)
|
|
1115
1126
|
lines = [
|
|
1116
|
-
|
|
1127
|
+
_agent_prompt(wake_mode),
|
|
1117
1128
|
"",
|
|
1129
|
+
f"Wake mode: {wake_mode.replace('_', ' ')}",
|
|
1118
1130
|
f"Current UTC time: {format_utc(now)}",
|
|
1119
1131
|
f"Agent name: {meta['name']}",
|
|
1120
1132
|
f"Stop policy: {meta['stop_policy']}",
|
|
@@ -1670,6 +1682,28 @@ def _include_full_goal_prompt(state, session):
|
|
|
1670
1682
|
return not ((session.get("thread_id") or state.get("thread_id") or "").strip())
|
|
1671
1683
|
|
|
1672
1684
|
|
|
1685
|
+
def _wake_mode(state, session):
|
|
1686
|
+
markers = (
|
|
1687
|
+
state.get("last_wake_at"),
|
|
1688
|
+
state.get("last_success_at"),
|
|
1689
|
+
state.get("reply"),
|
|
1690
|
+
state.get("update"),
|
|
1691
|
+
state.get("last_error"),
|
|
1692
|
+
state.get("thread_id"),
|
|
1693
|
+
session.get("thread_id"),
|
|
1694
|
+
)
|
|
1695
|
+
for value in markers:
|
|
1696
|
+
if (value or "").strip():
|
|
1697
|
+
return "continuation"
|
|
1698
|
+
return "first_wake"
|
|
1699
|
+
|
|
1700
|
+
|
|
1701
|
+
def _agent_prompt(wake_mode):
|
|
1702
|
+
if wake_mode == "continuation":
|
|
1703
|
+
return _CONTINUATION_PROMPT + _AGENT_PROMPT_TAIL
|
|
1704
|
+
return _FIRST_WAKE_PROMPT + _AGENT_PROMPT_TAIL
|
|
1705
|
+
|
|
1706
|
+
|
|
1673
1707
|
def _wake_facts(state):
|
|
1674
1708
|
facts = []
|
|
1675
1709
|
previous_status = (state.get("activity") or "").strip()
|
|
@@ -189,6 +189,7 @@ class AgentsTests(unittest.TestCase):
|
|
|
189
189
|
agent_dir,
|
|
190
190
|
)
|
|
191
191
|
self.assertIn("Agentbook (header + latest notes):", prompt)
|
|
192
|
+
self.assertIn("Wake mode: continuation", prompt)
|
|
192
193
|
self.assertIn("## Purpose", prompt)
|
|
193
194
|
self.assertIn("Hold the whole.", prompt)
|
|
194
195
|
self.assertIn("Watch for the real issue.", prompt)
|
|
@@ -196,8 +197,10 @@ class AgentsTests(unittest.TestCase):
|
|
|
196
197
|
self.assertIn("Previous status: Watching", prompt)
|
|
197
198
|
self.assertIn("Previous update: Still narrowing the field.", prompt)
|
|
198
199
|
self.assertIn("[... older notes omitted ...]", prompt)
|
|
200
|
+
self.assertIn("You are an independent codexapi agent continuing this job.", prompt)
|
|
199
201
|
self.assertNotIn("Original instructions:", prompt)
|
|
200
202
|
self.assertNotIn("OLD alpha alpha alpha alpha alpha alpha alpha alpha alpha alpha alpha alpha alpha alpha alpha alpha alpha alpha", prompt)
|
|
203
|
+
self.assertNotIn("resuming stewardship", prompt)
|
|
201
204
|
|
|
202
205
|
def test_build_wake_prompt_repairs_legacy_agentbook_before_wake(self):
|
|
203
206
|
with _temp_home() as home:
|
|
@@ -235,9 +238,33 @@ class AgentsTests(unittest.TestCase):
|
|
|
235
238
|
self.assertIn("The durable agentbook header was restored automatically on wake", repaired)
|
|
236
239
|
self.assertIn("Legacy note about the real issue.", repaired)
|
|
237
240
|
self.assertIn("## Purpose", prompt)
|
|
241
|
+
self.assertIn("Wake mode: continuation", prompt)
|
|
238
242
|
self.assertIn("Keep the true goal in view.", prompt)
|
|
239
243
|
self.assertNotIn("Original instructions:", prompt)
|
|
240
244
|
|
|
245
|
+
def test_build_wake_prompt_marks_first_wake_without_prior_history(self):
|
|
246
|
+
with _temp_home() as home:
|
|
247
|
+
agent = start_agent("Start from what is actually shown.", hostname="host-a")
|
|
248
|
+
agent_dir = home / "agents" / agent["id"]
|
|
249
|
+
meta = json.loads((agent_dir / "meta.json").read_text(encoding="utf-8"))
|
|
250
|
+
state = json.loads((agent_dir / "state.json").read_text(encoding="utf-8"))
|
|
251
|
+
session = json.loads((agent_dir / "hosts" / "host-a" / "session.json").read_text(encoding="utf-8"))
|
|
252
|
+
|
|
253
|
+
prompt = _build_wake_prompt(
|
|
254
|
+
meta,
|
|
255
|
+
state,
|
|
256
|
+
session,
|
|
257
|
+
datetime(2026, 3, 23, 9, 30, tzinfo=timezone.utc),
|
|
258
|
+
[],
|
|
259
|
+
agent_dir,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
self.assertIn("Wake mode: first wake", prompt)
|
|
263
|
+
self.assertIn("You are an independent codexapi agent starting this job.", prompt)
|
|
264
|
+
self.assertIn("Do not assume prior progress unless it is shown here.", prompt)
|
|
265
|
+
self.assertIn("Original instructions:", prompt)
|
|
266
|
+
self.assertNotIn("resuming stewardship", prompt)
|
|
267
|
+
|
|
241
268
|
def test_leadbook_block_shows_header_and_latest_notes(self):
|
|
242
269
|
leadbook = "\n".join(
|
|
243
270
|
[
|
|
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
|
|
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
|