codex-agent-framework 0.1.9__tar.gz → 0.1.10__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 (63) hide show
  1. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/CHANGELOG.md +8 -0
  2. {codex_agent_framework-0.1.9/codex_agent_framework.egg-info → codex_agent_framework-0.1.10}/PKG-INFO +2 -1
  3. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/README.md +1 -0
  4. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/agent.py +8 -2
  5. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/agent_runtime.py +6 -0
  6. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/message.py +18 -3
  7. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10/codex_agent_framework.egg-info}/PKG-INFO +2 -1
  8. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/pyproject.toml +1 -1
  9. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_agent.py +39 -2
  10. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_messages.py +19 -3
  11. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_server.py +22 -0
  12. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/LICENSE +0 -0
  13. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/MANIFEST.in +0 -0
  14. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/__init__.py +0 -0
  15. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/__main__.py +0 -0
  16. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/ai.py +0 -0
  17. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/builtin_commands.py +0 -0
  18. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/builtin_providers.py +0 -0
  19. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/builtin_tools.py +0 -0
  20. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/chat.py +0 -0
  21. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/client.py +0 -0
  22. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/command.py +0 -0
  23. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/event.py +0 -0
  24. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/get_text/__init__.py +0 -0
  25. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/get_text/default_gitignore +0 -0
  26. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/get_text/get_text.py +0 -0
  27. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/get_text/simpler_get_text.py +0 -0
  28. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/get_webdriver.py +0 -0
  29. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/image.py +0 -0
  30. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/latex.py +0 -0
  31. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/memory.py +0 -0
  32. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/prompts/image_generation_system_prompt.txt +0 -0
  33. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/prompts/system_prompt.txt +0 -0
  34. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/provider.py +0 -0
  35. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/scheduler.py +0 -0
  36. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/server.py +0 -0
  37. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/service.py +0 -0
  38. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/stream_utils.py +0 -0
  39. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/tool.py +0 -0
  40. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/tray.py +0 -0
  41. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/tui.py +0 -0
  42. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/utils.py +0 -0
  43. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/voice.py +0 -0
  44. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent/worker.py +0 -0
  45. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent_framework.egg-info/SOURCES.txt +0 -0
  46. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
  47. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent_framework.egg-info/entry_points.txt +0 -0
  48. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent_framework.egg-info/requires.txt +0 -0
  49. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/codex_agent_framework.egg-info/top_level.txt +0 -0
  50. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/setup.cfg +0 -0
  51. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_ai.py +0 -0
  52. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_chat.py +0 -0
  53. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_cli.py +0 -0
  54. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_client.py +0 -0
  55. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_events.py +0 -0
  56. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_image_message.py +0 -0
  57. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_memory.py +0 -0
  58. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_scheduler.py +0 -0
  59. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_service.py +0 -0
  60. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_tray.py +0 -0
  61. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_tui.py +0 -0
  62. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_utils.py +0 -0
  63. {codex_agent_framework-0.1.9 → codex_agent_framework-0.1.10}/tests/test_worker.py +0 -0
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
5
  This project loosely follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and uses semantic versioning where practical.
6
+ ## [0.1.10] - 2026-05-05
7
+ ### Fixed
8
+ - Persist only backend `compaction_summary` items in compaction messages, avoiding bulky compacted conversation payloads while preserving summary replay.
9
+ - Sanitize encrypted compaction summaries in local token estimates and refresh cached context status after compaction/message updates.
10
+
11
+ ### Tests
12
+ - Add regression coverage for compaction payload filtering, legacy `output_items` migration, context status cache invalidation, and runtime status updates after compaction.
13
+
6
14
  ## [0.1.9] - 2026-05-05
7
15
  ### Changed
8
16
  - Maintenance release for the packaged distribution after validating the updated local execution environment and deploy workflow.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-agent-framework
3
- Version: 0.1.9
3
+ Version: 0.1.10
4
4
  Summary: A lightweight event-driven Codex agent runtime.
5
5
  Author: Baptiste
6
6
  License-Expression: MIT
@@ -400,6 +400,7 @@ python -m build
400
400
  The distribution includes prompt text files and `codex_agent/get_text/default_gitignore` through package data and `MANIFEST.in`.
401
401
 
402
402
  ## Recent changes
403
+ - `0.1.10`: persist only backend compaction summaries, drop bulky compacted conversation payloads, and refresh context status after compaction.
403
404
  - `0.1.9`: maintenance packaging release after validating the local execution environment and deploy workflow.
404
405
  - `0.1.8`: scope TUI replay/SSE catch-up to the active session and make bash/python subprocesses inherit the project Python environment, including service-launched agents.
405
406
  - `0.1.7`: add durable RAG memory, scheduled wakeups, process-isolated server runtime, tray/service controls, robust SSE replay/reconnect, richer TUI status, and improved token estimates.
@@ -352,6 +352,7 @@ python -m build
352
352
  The distribution includes prompt text files and `codex_agent/get_text/default_gitignore` through package data and `MANIFEST.in`.
353
353
 
354
354
  ## Recent changes
355
+ - `0.1.10`: persist only backend compaction summaries, drop bulky compacted conversation payloads, and refresh context status after compaction.
355
356
  - `0.1.9`: maintenance packaging release after validating the local execution environment and deploy workflow.
356
357
  - `0.1.8`: scope TUI replay/SSE catch-up to the active session and make bash/python subprocesses inherit the project Python environment, including service-launched agents.
357
358
  - `0.1.7`: add durable RAG memory, scheduled wakeups, process-isolated server runtime, tray/service controls, robust SSE replay/reconnect, richer TUI status, and improved token estimates.
@@ -192,7 +192,7 @@ class AgentSession(modict):
192
192
  result = client.compact(history, model=model, instructions=instructions)
193
193
  message = CompactionMessage(
194
194
  response_id=result.response_id,
195
- output_items=result.output_items,
195
+ summaries=CompactionMessage.summary_items(result.output_items),
196
196
  compacted_message_count=len(compacted_messages),
197
197
  )
198
198
 
@@ -825,10 +825,11 @@ class Agent:
825
825
  compact_kwargs.client = client
826
826
  message = self.session.compact(**compact_kwargs)
827
827
  self._context_status_cache = None
828
- self.refresh_estimated_status_cache()
828
+ status = self.refresh_estimated_status_cache()
829
829
  self.emit(
830
830
  CompactionCompletedEvent,
831
831
  message=message,
832
+ status=status,
832
833
  compacted_message_count=plan.compacted_message_count,
833
834
  compacted_token_count=plan.compacted_token_count,
834
835
  post_compaction_message_count=len(self.session.context_messages()),
@@ -872,6 +873,7 @@ class Agent:
872
873
  if not msg.session_id:
873
874
  msg.session_id=self.current_session_id
874
875
  self.session.messages.append(msg)
876
+ self._context_status_cache = None
875
877
  self.save_session()
876
878
  self.emit(MessageAddedEvent, message=msg)
877
879
  return msg
@@ -1496,6 +1498,10 @@ class Agent:
1496
1498
  item = item.copy()
1497
1499
  item["image_url"] = "<image>"
1498
1500
  return item
1501
+ if item.get("type") == "compaction_summary" and item.get("encrypted_content"):
1502
+ item = item.copy()
1503
+ item["encrypted_content"] = "<encrypted_compaction_summary>"
1504
+ return {key: sanitize(child) for key, child in item.items()}
1499
1505
  return {key: sanitize(child) for key, child in item.items()}
1500
1506
  return value
1501
1507
 
@@ -247,6 +247,12 @@ class ProcessAgentRuntime:
247
247
  elif event_type == "session_loaded_event":
248
248
  self.cache.current_session_id = event.get("session_id")
249
249
  self.cache.session = event.get("session") or self.cache.session
250
+ elif event_type == "compaction_completed_event" and event.get("status"):
251
+ status = modict(event.status)
252
+ self.cache.status = status
253
+ self.cache.busy = bool(status.get("busy", self.cache.busy))
254
+ self.cache.current_turn_id = status.get("current_turn_id", self.cache.current_turn_id)
255
+ self.cache.current_session_id = status.get("session_id", self.cache.current_session_id)
250
256
  for handler in list(self.event_handlers):
251
257
  handler(event)
252
258
 
@@ -185,11 +185,26 @@ class CompactionMessage(DeveloperMessage):
185
185
  name = "compaction"
186
186
  content = "Previous conversation context was compacted by the backend."
187
187
  response_id = ""
188
- output_items = None
188
+ summaries = modict.factory(list)
189
189
  compacted_message_count = 0
190
190
 
191
+ def __init__(self, *args, **kwargs):
192
+ output_items = kwargs.pop("output_items", None)
193
+ super().__init__(*args, **kwargs)
194
+ if output_items is None:
195
+ output_items = self.pop("output_items", None)
196
+ if output_items is not None and not self.get("summaries"):
197
+ self.summaries = self.summary_items(output_items)
198
+
199
+ @staticmethod
200
+ def summary_items(items):
201
+ return [
202
+ item for item in items or []
203
+ if item.get("type") == "compaction_summary"
204
+ ]
205
+
191
206
  def to_response_format(self):
192
- return list(self.get("output_items") or [])
207
+ return list(self.get("summaries") or [])
193
208
 
194
209
  def as_string_attrs(self):
195
210
  attrs = self.extract('name', 'type', 'timestamp')
@@ -199,7 +214,7 @@ class CompactionMessage(DeveloperMessage):
199
214
 
200
215
  def as_string_body(self):
201
216
  summary = next(
202
- (item for item in self.get("output_items") or [] if item.get("type") == "compaction_summary"),
217
+ iter(self.get("summaries") or []),
203
218
  None,
204
219
  )
205
220
  if not summary:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-agent-framework
3
- Version: 0.1.9
3
+ Version: 0.1.10
4
4
  Summary: A lightweight event-driven Codex agent runtime.
5
5
  Author: Baptiste
6
6
  License-Expression: MIT
@@ -400,6 +400,7 @@ python -m build
400
400
  The distribution includes prompt text files and `codex_agent/get_text/default_gitignore` through package data and `MANIFEST.in`.
401
401
 
402
402
  ## Recent changes
403
+ - `0.1.10`: persist only backend compaction summaries, drop bulky compacted conversation payloads, and refresh context status after compaction.
403
404
  - `0.1.9`: maintenance packaging release after validating the local execution environment and deploy workflow.
404
405
  - `0.1.8`: scope TUI replay/SSE catch-up to the active session and make bash/python subprocesses inherit the project Python environment, including service-launched agents.
405
406
  - `0.1.7`: add durable RAG memory, scheduled wakeups, process-isolated server runtime, tray/service controls, robust SSE replay/reconnect, richer TUI status, and improved token estimates.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codex-agent-framework"
7
- version = "0.1.9"
7
+ version = "0.1.10"
8
8
  description = "A lightweight event-driven Codex agent runtime."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -120,7 +120,10 @@ class AbortableAI:
120
120
 
121
121
  class FakeCompactionResult:
122
122
  response_id = "resp_123"
123
- output_items = [{"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"}]
123
+ output_items = [
124
+ {"type": "message", "role": "user", "content": [{"type": "input_text", "text": "old"}]},
125
+ {"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"},
126
+ ]
124
127
 
125
128
 
126
129
  class FakeCompactionClient:
@@ -271,7 +274,8 @@ def test_agent_session_compact_inserts_compaction_before_remainder(tmp_path, mon
271
274
 
272
275
  assert isinstance(message, CompactionMessage)
273
276
  assert session.messages == [user, done, message, current]
274
- assert message.output_items == FakeCompactionResult.output_items
277
+ assert "output_items" not in message
278
+ assert message.summaries == [FakeCompactionResult.output_items[1]]
275
279
  assert message.compacted_message_count == 2
276
280
  assert client.history == [
277
281
  {
@@ -338,6 +342,8 @@ def test_agent_compact_session_emits_volume_events(tmp_path, monkeypatch):
338
342
  assert seen[0].total_session_messages == 3
339
343
  assert seen[0].compacted_token_count > 0
340
344
  assert seen[1].message is message
345
+ assert seen[1].status.source == "local_estimate"
346
+ assert seen[1].status.context_current_tokens != 5000
341
347
  assert seen[1].compacted_message_count == 2
342
348
  assert seen[1].post_compaction_message_count == 2
343
349
  assert seen[1].total_session_messages == 4
@@ -350,6 +356,17 @@ def test_agent_compact_session_emits_volume_events(tmp_path, monkeypatch):
350
356
  assert status.quota_7d_percent == 34
351
357
 
352
358
 
359
+ def test_agent_add_message_invalidates_context_status_cache():
360
+ agent = Agent(session="new", voice_enabled=False)
361
+ first = agent.context_status()
362
+
363
+ agent.add_message(DeveloperMessage(content="new visible context"))
364
+ second = agent.context_status()
365
+
366
+ assert second.total_session_messages == first.total_session_messages + 1
367
+ assert second.used_tokens != first.used_tokens
368
+
369
+
353
370
  def test_agent_get_context_starts_at_latest_compaction():
354
371
  agent = Agent(session="new", voice_enabled=False)
355
372
  old = DeveloperMessage(content="old hidden context")
@@ -367,6 +384,26 @@ def test_agent_get_context_starts_at_latest_compaction():
367
384
  assert "old hidden context" not in context_text
368
385
 
369
386
 
387
+ def test_agent_local_context_estimate_does_not_count_encrypted_compaction_payload():
388
+ agent = Agent(session="new", voice_enabled=False)
389
+ encrypted_content = "x" * 100_000
390
+ agent.session.messages = [
391
+ CompactionMessage(output_items=[{
392
+ "type": "compaction_summary",
393
+ "id": "cs_123",
394
+ "encrypted_content": encrypted_content,
395
+ }]),
396
+ DeveloperMessage(content="small current context"),
397
+ ]
398
+
399
+ status = agent.context_status()
400
+
401
+ assert status.used_tokens < 10_000
402
+ assert encrypted_content not in str(agent.local_token_count_payload(
403
+ agent.response_input_token_payload(agent.session.context_messages(), response_tools=[])
404
+ ))
405
+
406
+
370
407
  def test_agent_get_context_filters_expired_messages():
371
408
  agent = Agent(session="new", voice_enabled=False)
372
409
  expired = DeveloperMessage(content="expired hidden context", turns_left=0)
@@ -189,15 +189,18 @@ def test_server_tool_response_message_as_string_includes_item_metadata():
189
189
  assert '"type": "web_search_call"' in text
190
190
 
191
191
 
192
- def test_compaction_message_replays_backend_output_items():
193
- output_items = [{"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"}]
192
+ def test_compaction_message_replays_only_backend_summary_items():
193
+ output_items = [
194
+ {"type": "message", "role": "user", "content": [{"type": "input_text", "text": "old"}]},
195
+ {"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"},
196
+ ]
194
197
  message = CompactionMessage(
195
198
  response_id="resp_123",
196
199
  output_items=output_items,
197
200
  compacted_message_count=4,
198
201
  )
199
202
 
200
- assert message.to_response_format() == output_items
203
+ assert message.to_response_format() == [output_items[1]]
201
204
  text = message.as_string()
202
205
  assert text.startswith("<CompactionMessage ")
203
206
  assert "response_id='resp_123'" in text
@@ -207,6 +210,19 @@ def test_compaction_message_replays_backend_output_items():
207
210
  assert "encrypted_content" not in text
208
211
 
209
212
 
213
+ def test_compaction_message_migrates_legacy_output_items_without_persisting_them():
214
+ output_items = [
215
+ {"type": "message", "role": "user", "content": [{"type": "input_text", "text": "old"}]},
216
+ {"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"},
217
+ ]
218
+
219
+ message = CompactionMessage({"output_items": output_items})
220
+
221
+ assert "output_items" not in message
222
+ assert message.summaries == [output_items[1]]
223
+ assert message.to_response_format() == [output_items[1]]
224
+
225
+
210
226
  def test_image_generation_response_message_persists_result_as_file(tmp_path, monkeypatch):
211
227
  monkeypatch.setattr(utils, "RUNTIME_DIR", str(tmp_path))
212
228
  result = "Zm9v"
@@ -605,6 +605,28 @@ def test_process_agent_runtime_discards_timed_out_commands():
605
605
  assert runtime.pending == {}
606
606
 
607
607
 
608
+ def test_process_agent_runtime_updates_cached_status_from_compaction_event():
609
+ runtime = ProcessAgentRuntime()
610
+
611
+ runtime._handle_event(modict(
612
+ type="compaction_completed_event",
613
+ status=modict(
614
+ context_current_tokens=123,
615
+ context_limit_tokens=1000,
616
+ context_percent=12.3,
617
+ sent_session_messages=1,
618
+ total_session_messages=52,
619
+ session_id="session_1",
620
+ busy=False,
621
+ source="local_estimate",
622
+ ),
623
+ ))
624
+
625
+ assert runtime.cache.status.context_current_tokens == 123
626
+ assert runtime.cache.current_session_id == "session_1"
627
+ assert runtime.cache.busy is False
628
+
629
+
608
630
  def test_agent_server_subscribe_replays_missed_events_after_sequence():
609
631
  agent = Agent(session="new", voice_enabled=False)
610
632
  service = AgentServer(agent)